简体   繁体   中英

Nested Typescript Map Type

With Typescript 3.5.1, I can't seem to get the type of values to compile. For the following:

type foo = 'a' | 'b'

const x: Map<foo, {[key: string]: string}> = new Map([
  ['a', {'ok': 'so'}],
  ['b', {'nah': 'right'}]
])

tsc barfs up

const x: Map<foo, {
    [key: string]: string;
}>
Type 'Map<"a" | "b", { 'ok': string; 'nah'?: undefined; } | { 'nah': string; 'ok'?: undefined; }>' is not assignable to type 'Map<foo, { [key: string]: string; }>'.
  Type '{ 'ok': string; 'nah'?: undefined; } | { 'nah': string; 'ok'?: undefined; }' is not assignable to type '{ [key: string]: string; }'.
    Type '{ 'ok': string; 'nah'?: undefined; }' is not assignable to type '{ [key: string]: string; }'.
      Property ''nah'' is incompatible with index signature.
        Type 'undefined' is not assignable to type 'string'.

Is this a compiler bug, or am I doing something wrong to make the Map's value generic look like a union type?

A link to the typescript playground

I'm pretty sure this is not a bug. Typescript narrows the type of your right side down to {ok: string, nah?: undefined} | {ok?: undefined, nah: string} {ok: string, nah?: undefined} | {ok?: undefined, nah: string} , since you have two objects that are missing the property of the other. The one that has only "ok" will return undefined if "nah" is accessed and vice-versa.

This means that the initial type {[key: string]: string} is not valid anymore, because the properties can either return a string ('so' and 'right' are subtypes of string) or undefined.

The simpliest way around this is to allow these undefined values by using Map<foo, {[key: string]: string | undefined}> Map<foo, {[key: string]: string | undefined}> .

Edit: Corrected the inferred type - thanks to jcalz for pointing that out!

TypeScript's type inference can be a bit confusing sometimes. It is never perfect and the TypeScript team had to make hard choices. You get one of these choices that does not match your expectations...

You can get ride of type inference by explicitly specifying the generic types:

const x = new Map<foo, {[key: string]: string}>([
  ['a', {'ok': 'so'}],
  ['b', {'nah': 'right'}]
])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM