diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a59c5b89ecf..ff2360d57a0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6444,7 +6444,8 @@ namespace ts { for (const declaration of symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { for (const member of (declaration).members) { - const memberType = getFreshTypeOfLiteralType(getLiteralType(getEnumMemberValue(member)!, enumCount, getSymbolOfNode(member))); // TODO: GH#18217 + const value = getEnumMemberValue(member); + const memberType = getFreshTypeOfLiteralType(getLiteralType(value !== undefined ? value : 0, enumCount, getSymbolOfNode(member))); getSymbolLinks(getSymbolOfNode(member)).declaredType = memberType; memberTypeList.push(getRegularTypeOfLiteralType(memberType)); } @@ -7453,8 +7454,9 @@ namespace ts { else if (t.flags & (TypeFlags.Any | TypeFlags.String)) { stringIndexInfo = createIndexInfo(propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly)); } - else if (t.flags & TypeFlags.Number) { - numberIndexInfo = createIndexInfo(propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly)); + else if (t.flags & (TypeFlags.Number | TypeFlags.Enum)) { + numberIndexInfo = createIndexInfo(numberIndexInfo ? getUnionType([numberIndexInfo.type, propType]) : propType, + !!(templateModifiers & MappedTypeModifiers.IncludeReadonly)); } } } @@ -28472,9 +28474,9 @@ namespace ts { if (member.initializer) { return computeConstantValue(member); } - // In ambient enum declarations that specify no const modifier, enum member declarations that omit - // a value are considered computed members (as opposed to having auto-incremented values). - if (member.parent.flags & NodeFlags.Ambient && !isEnumConst(member.parent)) { + // In ambient non-const numeric enum declarations, enum members without initializers are + // considered computed members (as opposed to having auto-incremented values). + if (member.parent.flags & NodeFlags.Ambient && !isEnumConst(member.parent) && getEnumKind(getSymbolOfNode(member.parent)) === EnumKind.Numeric) { return undefined; } // If the member declaration specifies no value, the member is considered a constant enum member. diff --git a/tests/baselines/reference/numericEnumMappedType.js b/tests/baselines/reference/numericEnumMappedType.js new file mode 100644 index 00000000000..78a13e443ff --- /dev/null +++ b/tests/baselines/reference/numericEnumMappedType.js @@ -0,0 +1,110 @@ +//// [numericEnumMappedType.ts] +// Repro from #31771 + +enum E1 { ONE, TWO, THREE } +declare enum E2 { ONE, TWO, THREE } + +type Bins1 = { [k in E1]?: string; } +type Bins2 = { [k in E2]?: string; } + +const b1: Bins1 = {}; +const b2: Bins2 = {}; + +const e1: E1 = E1.ONE; +const e2: E2 = E2.ONE; + +b1[1] = "a"; +b1[e1] = "b"; + +b2[1] = "a"; +b2[e2] = "b"; + +// Multiple numeric enum types accrue to the same numeric index signature in a mapped type + +declare function val(): number; + +enum N1 { A = val(), B = val() } +enum N2 { C = val(), D = val() } + +type T1 = { [K in N1 | N2]: K }; + +// Enum types with string valued members are always literal enum types and therefore +// ONE and TWO below are not computed members but rather just numerically valued members +// with auto-incremented values. + +declare enum E { ONE, TWO, THREE = 'x' } +const e: E = E.ONE; +const x: E.ONE = e; + + +//// [numericEnumMappedType.js] +"use strict"; +// Repro from #31771 +var E1; +(function (E1) { + E1[E1["ONE"] = 0] = "ONE"; + E1[E1["TWO"] = 1] = "TWO"; + E1[E1["THREE"] = 2] = "THREE"; +})(E1 || (E1 = {})); +var b1 = {}; +var b2 = {}; +var e1 = E1.ONE; +var e2 = E2.ONE; +b1[1] = "a"; +b1[e1] = "b"; +b2[1] = "a"; +b2[e2] = "b"; +var N1; +(function (N1) { + N1[N1["A"] = val()] = "A"; + N1[N1["B"] = val()] = "B"; +})(N1 || (N1 = {})); +var N2; +(function (N2) { + N2[N2["C"] = val()] = "C"; + N2[N2["D"] = val()] = "D"; +})(N2 || (N2 = {})); +var e = E.ONE; +var x = e; + + +//// [numericEnumMappedType.d.ts] +declare enum E1 { + ONE = 0, + TWO = 1, + THREE = 2 +} +declare enum E2 { + ONE, + TWO, + THREE +} +declare type Bins1 = { + [k in E1]?: string; +}; +declare type Bins2 = { + [k in E2]?: string; +}; +declare const b1: Bins1; +declare const b2: Bins2; +declare const e1: E1; +declare const e2: E2; +declare function val(): number; +declare enum N1 { + A, + B +} +declare enum N2 { + C, + D +} +declare type T1 = { + [K in N1 | N2]: K; +}; +declare enum E { + ONE = 0, + TWO = 1, + THREE = "x" +} +declare const e: E; +declare const x: E.ONE; diff --git a/tests/baselines/reference/numericEnumMappedType.symbols b/tests/baselines/reference/numericEnumMappedType.symbols new file mode 100644 index 00000000000..08979b3bed1 --- /dev/null +++ b/tests/baselines/reference/numericEnumMappedType.symbols @@ -0,0 +1,111 @@ +=== tests/cases/compiler/numericEnumMappedType.ts === +// Repro from #31771 + +enum E1 { ONE, TWO, THREE } +>E1 : Symbol(E1, Decl(numericEnumMappedType.ts, 0, 0)) +>ONE : Symbol(E1.ONE, Decl(numericEnumMappedType.ts, 2, 9)) +>TWO : Symbol(E1.TWO, Decl(numericEnumMappedType.ts, 2, 14)) +>THREE : Symbol(E1.THREE, Decl(numericEnumMappedType.ts, 2, 19)) + +declare enum E2 { ONE, TWO, THREE } +>E2 : Symbol(E2, Decl(numericEnumMappedType.ts, 2, 27)) +>ONE : Symbol(E2.ONE, Decl(numericEnumMappedType.ts, 3, 17)) +>TWO : Symbol(E2.TWO, Decl(numericEnumMappedType.ts, 3, 22)) +>THREE : Symbol(E2.THREE, Decl(numericEnumMappedType.ts, 3, 27)) + +type Bins1 = { [k in E1]?: string; } +>Bins1 : Symbol(Bins1, Decl(numericEnumMappedType.ts, 3, 35)) +>k : Symbol(k, Decl(numericEnumMappedType.ts, 5, 16)) +>E1 : Symbol(E1, Decl(numericEnumMappedType.ts, 0, 0)) + +type Bins2 = { [k in E2]?: string; } +>Bins2 : Symbol(Bins2, Decl(numericEnumMappedType.ts, 5, 36)) +>k : Symbol(k, Decl(numericEnumMappedType.ts, 6, 16)) +>E2 : Symbol(E2, Decl(numericEnumMappedType.ts, 2, 27)) + +const b1: Bins1 = {}; +>b1 : Symbol(b1, Decl(numericEnumMappedType.ts, 8, 5)) +>Bins1 : Symbol(Bins1, Decl(numericEnumMappedType.ts, 3, 35)) + +const b2: Bins2 = {}; +>b2 : Symbol(b2, Decl(numericEnumMappedType.ts, 9, 5)) +>Bins2 : Symbol(Bins2, Decl(numericEnumMappedType.ts, 5, 36)) + +const e1: E1 = E1.ONE; +>e1 : Symbol(e1, Decl(numericEnumMappedType.ts, 11, 5)) +>E1 : Symbol(E1, Decl(numericEnumMappedType.ts, 0, 0)) +>E1.ONE : Symbol(E1.ONE, Decl(numericEnumMappedType.ts, 2, 9)) +>E1 : Symbol(E1, Decl(numericEnumMappedType.ts, 0, 0)) +>ONE : Symbol(E1.ONE, Decl(numericEnumMappedType.ts, 2, 9)) + +const e2: E2 = E2.ONE; +>e2 : Symbol(e2, Decl(numericEnumMappedType.ts, 12, 5)) +>E2 : Symbol(E2, Decl(numericEnumMappedType.ts, 2, 27)) +>E2.ONE : Symbol(E2.ONE, Decl(numericEnumMappedType.ts, 3, 17)) +>E2 : Symbol(E2, Decl(numericEnumMappedType.ts, 2, 27)) +>ONE : Symbol(E2.ONE, Decl(numericEnumMappedType.ts, 3, 17)) + +b1[1] = "a"; +>b1 : Symbol(b1, Decl(numericEnumMappedType.ts, 8, 5)) +>1 : Symbol(1) + +b1[e1] = "b"; +>b1 : Symbol(b1, Decl(numericEnumMappedType.ts, 8, 5)) +>e1 : Symbol(e1, Decl(numericEnumMappedType.ts, 11, 5)) + +b2[1] = "a"; +>b2 : Symbol(b2, Decl(numericEnumMappedType.ts, 9, 5)) + +b2[e2] = "b"; +>b2 : Symbol(b2, Decl(numericEnumMappedType.ts, 9, 5)) +>e2 : Symbol(e2, Decl(numericEnumMappedType.ts, 12, 5)) + +// Multiple numeric enum types accrue to the same numeric index signature in a mapped type + +declare function val(): number; +>val : Symbol(val, Decl(numericEnumMappedType.ts, 18, 13)) + +enum N1 { A = val(), B = val() } +>N1 : Symbol(N1, Decl(numericEnumMappedType.ts, 22, 31)) +>A : Symbol(N1.A, Decl(numericEnumMappedType.ts, 24, 9)) +>val : Symbol(val, Decl(numericEnumMappedType.ts, 18, 13)) +>B : Symbol(N1.B, Decl(numericEnumMappedType.ts, 24, 20)) +>val : Symbol(val, Decl(numericEnumMappedType.ts, 18, 13)) + +enum N2 { C = val(), D = val() } +>N2 : Symbol(N2, Decl(numericEnumMappedType.ts, 24, 32)) +>C : Symbol(N2.C, Decl(numericEnumMappedType.ts, 25, 9)) +>val : Symbol(val, Decl(numericEnumMappedType.ts, 18, 13)) +>D : Symbol(N2.D, Decl(numericEnumMappedType.ts, 25, 20)) +>val : Symbol(val, Decl(numericEnumMappedType.ts, 18, 13)) + +type T1 = { [K in N1 | N2]: K }; +>T1 : Symbol(T1, Decl(numericEnumMappedType.ts, 25, 32)) +>K : Symbol(K, Decl(numericEnumMappedType.ts, 27, 13)) +>N1 : Symbol(N1, Decl(numericEnumMappedType.ts, 22, 31)) +>N2 : Symbol(N2, Decl(numericEnumMappedType.ts, 24, 32)) +>K : Symbol(K, Decl(numericEnumMappedType.ts, 27, 13)) + +// Enum types with string valued members are always literal enum types and therefore +// ONE and TWO below are not computed members but rather just numerically valued members +// with auto-incremented values. + +declare enum E { ONE, TWO, THREE = 'x' } +>E : Symbol(E, Decl(numericEnumMappedType.ts, 27, 32)) +>ONE : Symbol(E.ONE, Decl(numericEnumMappedType.ts, 33, 16)) +>TWO : Symbol(E.TWO, Decl(numericEnumMappedType.ts, 33, 21)) +>THREE : Symbol(E.THREE, Decl(numericEnumMappedType.ts, 33, 26)) + +const e: E = E.ONE; +>e : Symbol(e, Decl(numericEnumMappedType.ts, 34, 5)) +>E : Symbol(E, Decl(numericEnumMappedType.ts, 27, 32)) +>E.ONE : Symbol(E.ONE, Decl(numericEnumMappedType.ts, 33, 16)) +>E : Symbol(E, Decl(numericEnumMappedType.ts, 27, 32)) +>ONE : Symbol(E.ONE, Decl(numericEnumMappedType.ts, 33, 16)) + +const x: E.ONE = e; +>x : Symbol(x, Decl(numericEnumMappedType.ts, 35, 5)) +>E : Symbol(E, Decl(numericEnumMappedType.ts, 27, 32)) +>ONE : Symbol(E.ONE, Decl(numericEnumMappedType.ts, 33, 16)) +>e : Symbol(e, Decl(numericEnumMappedType.ts, 34, 5)) + diff --git a/tests/baselines/reference/numericEnumMappedType.types b/tests/baselines/reference/numericEnumMappedType.types new file mode 100644 index 00000000000..bffec2ed8ca --- /dev/null +++ b/tests/baselines/reference/numericEnumMappedType.types @@ -0,0 +1,117 @@ +=== tests/cases/compiler/numericEnumMappedType.ts === +// Repro from #31771 + +enum E1 { ONE, TWO, THREE } +>E1 : E1 +>ONE : E1.ONE +>TWO : E1.TWO +>THREE : E1.THREE + +declare enum E2 { ONE, TWO, THREE } +>E2 : E2 +>ONE : E2 +>TWO : E2 +>THREE : E2 + +type Bins1 = { [k in E1]?: string; } +>Bins1 : Bins1 + +type Bins2 = { [k in E2]?: string; } +>Bins2 : Bins2 + +const b1: Bins1 = {}; +>b1 : Bins1 +>{} : {} + +const b2: Bins2 = {}; +>b2 : Bins2 +>{} : {} + +const e1: E1 = E1.ONE; +>e1 : E1 +>E1.ONE : E1.ONE +>E1 : typeof E1 +>ONE : E1.ONE + +const e2: E2 = E2.ONE; +>e2 : E2 +>E2.ONE : E2 +>E2 : typeof E2 +>ONE : E2 + +b1[1] = "a"; +>b1[1] = "a" : "a" +>b1[1] : string | undefined +>b1 : Bins1 +>1 : 1 +>"a" : "a" + +b1[e1] = "b"; +>b1[e1] = "b" : "b" +>b1[e1] : string | undefined +>b1 : Bins1 +>e1 : E1.ONE +>"b" : "b" + +b2[1] = "a"; +>b2[1] = "a" : "a" +>b2[1] : string | undefined +>b2 : Bins2 +>1 : 1 +>"a" : "a" + +b2[e2] = "b"; +>b2[e2] = "b" : "b" +>b2[e2] : string | undefined +>b2 : Bins2 +>e2 : E2 +>"b" : "b" + +// Multiple numeric enum types accrue to the same numeric index signature in a mapped type + +declare function val(): number; +>val : () => number + +enum N1 { A = val(), B = val() } +>N1 : N1 +>A : N1 +>val() : number +>val : () => number +>B : N1 +>val() : number +>val : () => number + +enum N2 { C = val(), D = val() } +>N2 : N2 +>C : N2 +>val() : number +>val : () => number +>D : N2 +>val() : number +>val : () => number + +type T1 = { [K in N1 | N2]: K }; +>T1 : T1 + +// Enum types with string valued members are always literal enum types and therefore +// ONE and TWO below are not computed members but rather just numerically valued members +// with auto-incremented values. + +declare enum E { ONE, TWO, THREE = 'x' } +>E : E +>ONE : E.ONE +>TWO : E.TWO +>THREE : E.THREE +>'x' : "x" + +const e: E = E.ONE; +>e : E +>E.ONE : E.ONE +>E : typeof E +>ONE : E.ONE + +const x: E.ONE = e; +>x : E.ONE +>E : any +>e : E.ONE + diff --git a/tests/baselines/reference/user/chrome-devtools-frontend.log b/tests/baselines/reference/user/chrome-devtools-frontend.log index 6b6b46ab180..7c51aef5159 100644 --- a/tests/baselines/reference/user/chrome-devtools-frontend.log +++ b/tests/baselines/reference/user/chrome-devtools-frontend.log @@ -3012,7 +3012,15 @@ node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighth node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65285,7): error TS2339: Property 'description' does not exist on type 'Error'. node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65310,1): error TS2323: Cannot redeclare exported variable '__esModule'. node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65379,1): error TS2323: Cannot redeclare exported variable '__esModule'. +node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65443,1): error TS2322: Type '{ line: number; column: number; }' is not assignable to type '{}'. + Property 'line' does not exist on type '{}'. +node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65464,1): error TS2322: Type '{ line: number; column: number; }' is not assignable to type '{}'. + Property 'line' does not exist on type '{}'. node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65468,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'entry' must be of type '{ multiLine: boolean; slice: any[]; range: number[]; loc: { start: { line: number; column: number; }; end: {}; }; }', but here has type '{ multiLine: boolean; slice: any[]; range: any[]; loc: { start: { line: number; column: number; }; end: {}; }; }'. +node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65507,1): error TS2322: Type '{ line: number; column: number; }' is not assignable to type '{}'. + Property 'line' does not exist on type '{}'. +node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65529,1): error TS2322: Type '{ line: number; column: number; }' is not assignable to type '{}'. + Property 'line' does not exist on type '{}'. node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65533,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'entry' must be of type '{ multiLine: boolean; slice: number[]; range: any[]; loc: { start: { line: number; column: number; }; end: {}; }; }', but here has type '{ multiLine: boolean; slice: any[]; range: any[]; loc: { start: { line: number; column: number; }; end: {}; }; }'. node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(65576,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'comment' must be of type '{ multiLine: boolean; slice: any[]; range: number[]; loc: { start: { line: number; column: number; }; end: {}; }; }[]', but here has type '{ multiLine: boolean; slice: number[]; range: any[]; loc: { start: { line: number; column: number; }; end: {}; }; }[]'. node_modules/chrome-devtools-frontend/front_end/audits2_worker/lighthouse/lighthouse-background.js(66529,1): error TS2323: Cannot redeclare exported variable '__esModule'. diff --git a/tests/baselines/reference/user/prettier.log b/tests/baselines/reference/user/prettier.log index 6ee6e91cb6b..2082b66ee59 100644 --- a/tests/baselines/reference/user/prettier.log +++ b/tests/baselines/reference/user/prettier.log @@ -160,6 +160,13 @@ src/language-js/printer-estree.js(1892,20): error TS2345: Argument of type '{ ty src/language-js/printer-estree.js(1894,18): error TS2345: Argument of type '"while ("' is not assignable to parameter of type '{ type: string; id: any; contents: any; break: boolean; expandedStates: any; }'. src/language-js/printer-estree.js(1903,9): error TS2345: Argument of type '")"' is not assignable to parameter of type '{ type: string; id: any; contents: any; break: boolean; expandedStates: any; }'. src/language-js/printer-estree.js(2426,28): error TS2345: Argument of type 'any' is not assignable to parameter of type 'never'. +src/language-js/printer-estree.js(2446,13): error TS2345: Argument of type '{ hasLineBreak: boolean; cells: never[]; }[]' is not assignable to parameter of type '{ cells: any; } | ConcatArray<{ cells: any; }>'. + Type '{ hasLineBreak: boolean; cells: never[]; }[]' is not assignable to type 'ConcatArray<{ cells: any; }>'. + Types of property 'slice' are incompatible. + Type '(start?: number | undefined, end?: number | undefined) => { hasLineBreak: boolean; cells: never[]; }[]' is not assignable to type '(start?: number | undefined, end?: number | undefined) => { cells: any; }[]'. + Type '{ hasLineBreak: boolean; cells: never[]; }[]' is not assignable to type '{ cells: any; }[]'. + Type '{ hasLineBreak: boolean; cells: never[]; }' is not assignable to type '{ cells: any; }'. + Property 'hasLineBreak' does not exist on type '{ cells: any; }'. src/language-js/printer-estree.js(3513,11): error TS2345: Argument of type 'never[] | { type: string; parts: any; }' is not assignable to parameter of type 'ConcatArray'. Type '{ type: string; parts: any; }' is not assignable to type 'ConcatArray'. src/language-js/printer-estree.js(4031,23): error TS2532: Object is possibly 'undefined'. diff --git a/tests/cases/compiler/numericEnumMappedType.ts b/tests/cases/compiler/numericEnumMappedType.ts new file mode 100644 index 00000000000..7c03991e6a2 --- /dev/null +++ b/tests/cases/compiler/numericEnumMappedType.ts @@ -0,0 +1,39 @@ +// @strict: true +// @declaration: true + +// Repro from #31771 + +enum E1 { ONE, TWO, THREE } +declare enum E2 { ONE, TWO, THREE } + +type Bins1 = { [k in E1]?: string; } +type Bins2 = { [k in E2]?: string; } + +const b1: Bins1 = {}; +const b2: Bins2 = {}; + +const e1: E1 = E1.ONE; +const e2: E2 = E2.ONE; + +b1[1] = "a"; +b1[e1] = "b"; + +b2[1] = "a"; +b2[e2] = "b"; + +// Multiple numeric enum types accrue to the same numeric index signature in a mapped type + +declare function val(): number; + +enum N1 { A = val(), B = val() } +enum N2 { C = val(), D = val() } + +type T1 = { [K in N1 | N2]: K }; + +// Enum types with string valued members are always literal enum types and therefore +// ONE and TWO below are not computed members but rather just numerically valued members +// with auto-incremented values. + +declare enum E { ONE, TWO, THREE = 'x' } +const e: E = E.ONE; +const x: E.ONE = e;