diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5eb14081e2a3c..ee3f0b21d6081 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18330,6 +18330,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type = prop.escapedName === InternalSymbolName.Default ? getStringLiteralType("default") : name && getLiteralTypeFromPropertyName(name) || (!isKnownSymbol(prop) ? getStringLiteralType(symbolName(prop)) : undefined); } + // Property name can only have a union type if it refers to multiple structurally identical declarations. (See addMemberForKeyTypeWorker) + // In this case, it's safe to take any of the constituents to compute the literal type. + if (type && type.flags & TypeFlags.Union) { + const unionType = type as UnionType; + if (unionType.types.length > 0) { + type = unionType.types[0]; + } + } if (type && type.flags & include) { return type; } diff --git a/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.js b/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.js new file mode 100644 index 0000000000000..3e7728ccb2cb7 --- /dev/null +++ b/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.js @@ -0,0 +1,44 @@ +//// [tests/cases/compiler/mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts] //// + +//// [mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts] +// https://github.com/microsoft/TypeScript/issues/41700 + +enum EnumA { + A = 'A', + B = 'B', +} + +// A second enum with at least one key also in EnumA +enum EnumB { + B = 'B', + C = 'C', +} + +type Mapped = { + [k in EnumA|EnumB]: string; +}; + +// Should work +const partial: Partial = { + [EnumA.B]: 'value', +}; + + +//// [mappedTypeOverMappedTypeWithOverlappingEnumKeys.js] +// https://github.com/microsoft/TypeScript/issues/41700 +var _a; +var EnumA; +(function (EnumA) { + EnumA["A"] = "A"; + EnumA["B"] = "B"; +})(EnumA || (EnumA = {})); +// A second enum with at least one key also in EnumA +var EnumB; +(function (EnumB) { + EnumB["B"] = "B"; + EnumB["C"] = "C"; +})(EnumB || (EnumB = {})); +// Should work +var partial = (_a = {}, + _a[EnumA.B] = 'value', + _a); diff --git a/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.symbols b/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.symbols new file mode 100644 index 0000000000000..0394b9cf4a8e5 --- /dev/null +++ b/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.symbols @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts] //// + +=== mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts === +// https://github.com/microsoft/TypeScript/issues/41700 + +enum EnumA { +>EnumA : Symbol(EnumA, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 0, 0)) + + A = 'A', +>A : Symbol(EnumA.A, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 2, 12)) + + B = 'B', +>B : Symbol(EnumA.B, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 3, 12)) +} + +// A second enum with at least one key also in EnumA +enum EnumB { +>EnumB : Symbol(EnumB, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 5, 1)) + + B = 'B', +>B : Symbol(EnumB.B, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 8, 12)) + + C = 'C', +>C : Symbol(EnumB.C, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 9, 12)) +} + +type Mapped = { +>Mapped : Symbol(Mapped, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 11, 1)) + + [k in EnumA|EnumB]: string; +>k : Symbol(k, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 14, 5)) +>EnumA : Symbol(EnumA, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 0, 0)) +>EnumB : Symbol(EnumB, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 5, 1)) + +}; + +// Should work +const partial: Partial = { +>partial : Symbol(partial, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 18, 5)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Mapped : Symbol(Mapped, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 11, 1)) + + [EnumA.B]: 'value', +>[EnumA.B] : Symbol([EnumA.B], Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 18, 34)) +>EnumA.B : Symbol(EnumA.B, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 3, 12)) +>EnumA : Symbol(EnumA, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 0, 0)) +>B : Symbol(EnumA.B, Decl(mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts, 3, 12)) + +}; + diff --git a/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.types b/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.types new file mode 100644 index 0000000000000..ccfb9c01c9a80 --- /dev/null +++ b/tests/baselines/reference/mappedTypeOverMappedTypeWithOverlappingEnumKeys.types @@ -0,0 +1,68 @@ +//// [tests/cases/compiler/mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts] //// + +=== mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts === +// https://github.com/microsoft/TypeScript/issues/41700 + +enum EnumA { +>EnumA : EnumA +> : ^^^^^ + + A = 'A', +>A : EnumA.A +> : ^^^^^^^ +>'A' : "A" +> : ^^^ + + B = 'B', +>B : EnumA.B +> : ^^^^^^^ +>'B' : "B" +> : ^^^ +} + +// A second enum with at least one key also in EnumA +enum EnumB { +>EnumB : EnumB +> : ^^^^^ + + B = 'B', +>B : EnumB.B +> : ^^^^^^^ +>'B' : "B" +> : ^^^ + + C = 'C', +>C : EnumB.C +> : ^^^^^^^ +>'C' : "C" +> : ^^^ +} + +type Mapped = { +>Mapped : Mapped +> : ^^^^^^ + + [k in EnumA|EnumB]: string; +}; + +// Should work +const partial: Partial = { +>partial : Partial +> : ^^^^^^^^^^^^^^^ +>{ [EnumA.B]: 'value',} : { B: string; } +> : ^^^^^^^^^^^^^^ + + [EnumA.B]: 'value', +>[EnumA.B] : string +> : ^^^^^^ +>EnumA.B : EnumA.B +> : ^^^^^^^ +>EnumA : typeof EnumA +> : ^^^^^^^^^^^^ +>B : EnumA.B +> : ^^^^^^^ +>'value' : "value" +> : ^^^^^^^ + +}; + diff --git a/tests/cases/compiler/mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts b/tests/cases/compiler/mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts new file mode 100644 index 0000000000000..952d4bc31b31e --- /dev/null +++ b/tests/cases/compiler/mappedTypeOverMappedTypeWithOverlappingEnumKeys.ts @@ -0,0 +1,21 @@ +// https://github.com/microsoft/TypeScript/issues/41700 + +enum EnumA { + A = 'A', + B = 'B', +} + +// A second enum with at least one key also in EnumA +enum EnumB { + B = 'B', + C = 'C', +} + +type Mapped = { + [k in EnumA|EnumB]: string; +}; + +// Should work +const partial: Partial = { + [EnumA.B]: 'value', +};