Skip to content

Commit 162713f

Browse files
authored
fix(40617): handle uninitialized class member with computed key (microsoft#45974)
1 parent f9ae305 commit 162713f

File tree

46 files changed

+1374
-164
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1374
-164
lines changed

src/compiler/binder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,7 @@ namespace ts {
889889
return isDottedName(expr)
890890
|| (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression)
891891
|| isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right)
892-
|| isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression)
892+
|| isElementAccessExpression(expr) && (isStringOrNumericLiteralLike(expr.argumentExpression) || isEntityNameExpression(expr.argumentExpression)) && isNarrowableReference(expr.expression)
893893
|| isAssignmentExpression(expr) && isNarrowableReference(expr.left);
894894
}
895895

src/compiler/checker.ts

+55-12
Original file line numberDiff line numberDiff line change
@@ -22877,9 +22877,10 @@ namespace ts {
2287722877
return isMatchingReference((source as NonNullExpression | ParenthesizedExpression).expression, target);
2287822878
case SyntaxKind.PropertyAccessExpression:
2287922879
case SyntaxKind.ElementAccessExpression:
22880-
return isAccessExpression(target) &&
22881-
getAccessedPropertyName(source as AccessExpression) === getAccessedPropertyName(target) &&
22882-
isMatchingReference((source as AccessExpression).expression, target.expression);
22880+
const sourcePropertyName = getAccessedPropertyName(source as AccessExpression);
22881+
const targetPropertyName = isAccessExpression(target) ? getAccessedPropertyName(target) : undefined;
22882+
return sourcePropertyName !== undefined && targetPropertyName !== undefined && targetPropertyName === sourcePropertyName &&
22883+
isMatchingReference((source as AccessExpression).expression, (target as AccessExpression).expression);
2288322884
case SyntaxKind.QualifiedName:
2288422885
return isAccessExpression(target) &&
2288522886
(source as QualifiedName).right.escapedText === getAccessedPropertyName(target) &&
@@ -22891,12 +22892,52 @@ namespace ts {
2289122892
}
2289222893

2289322894
function getAccessedPropertyName(access: AccessExpression | BindingElement | ParameterDeclaration): __String | undefined {
22894-
let propertyName;
22895-
return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
22896-
access.kind === SyntaxKind.ElementAccessExpression && isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
22897-
access.kind === SyntaxKind.BindingElement && (propertyName = getDestructuringPropertyName(access)) ? escapeLeadingUnderscores(propertyName) :
22898-
access.kind === SyntaxKind.Parameter ? ("" + access.parent.parameters.indexOf(access)) as __String :
22899-
undefined;
22895+
if (isPropertyAccessExpression(access)) {
22896+
return access.name.escapedText;
22897+
}
22898+
if (isElementAccessExpression(access)) {
22899+
return tryGetElementAccessExpressionName(access);
22900+
}
22901+
if (isBindingElement(access)) {
22902+
const name = getDestructuringPropertyName(access);
22903+
return name ? escapeLeadingUnderscores(name) : undefined;
22904+
}
22905+
if (isParameter(access)) {
22906+
return ("" + access.parent.parameters.indexOf(access)) as __String;
22907+
}
22908+
return undefined;
22909+
}
22910+
22911+
function tryGetNameFromType(type: Type) {
22912+
return type.flags & TypeFlags.UniqueESSymbol ? (type as UniqueESSymbolType).escapedName :
22913+
type.flags & TypeFlags.StringOrNumberLiteral ? escapeLeadingUnderscores("" + (type as StringLiteralType | NumberLiteralType).value) : undefined;
22914+
}
22915+
22916+
function tryGetElementAccessExpressionName(node: ElementAccessExpression) {
22917+
if (isStringOrNumericLiteralLike(node.argumentExpression)) {
22918+
return escapeLeadingUnderscores(node.argumentExpression.text);
22919+
}
22920+
if (isEntityNameExpression(node.argumentExpression)) {
22921+
const symbol = resolveEntityName(node.argumentExpression, SymbolFlags.Value, /*ignoreErrors*/ true);
22922+
if (!symbol || !isConstVariable(symbol)) return undefined;
22923+
22924+
const declaration = symbol.valueDeclaration;
22925+
if (declaration === undefined) return undefined;
22926+
22927+
const type = tryGetTypeFromEffectiveTypeNode(declaration);
22928+
if (type) {
22929+
const name = tryGetNameFromType(type);
22930+
if (name !== undefined) {
22931+
return name;
22932+
}
22933+
}
22934+
22935+
if (hasOnlyExpressionInitializer(declaration)) {
22936+
const initializer = getEffectiveInitializer(declaration);
22937+
return initializer && tryGetNameFromType(getTypeOfExpression(initializer));
22938+
}
22939+
}
22940+
return undefined;
2290022941
}
2290122942

2290222943
function containsMatchingReference(source: Node, target: Node) {
@@ -39529,7 +39570,7 @@ namespace ts {
3952939570
}
3953039571
if (!isStatic(member) && isPropertyWithoutInitializer(member)) {
3953139572
const propName = (member as PropertyDeclaration).name;
39532-
if (isIdentifier(propName) || isPrivateIdentifier(propName)) {
39573+
if (isIdentifier(propName) || isPrivateIdentifier(propName) || isComputedPropertyName(propName)) {
3953339574
const type = getTypeOfSymbol(getSymbolOfNode(member));
3953439575
if (!(type.flags & TypeFlags.AnyOrUnknown || getFalsyFlags(type) & TypeFlags.Undefined)) {
3953539576
if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) {
@@ -39565,8 +39606,10 @@ namespace ts {
3956539606
return false;
3956639607
}
3956739608

39568-
function isPropertyInitializedInConstructor(propName: Identifier | PrivateIdentifier, propType: Type, constructor: ConstructorDeclaration) {
39569-
const reference = factory.createPropertyAccessExpression(factory.createThis(), propName);
39609+
function isPropertyInitializedInConstructor(propName: Identifier | PrivateIdentifier | ComputedPropertyName, propType: Type, constructor: ConstructorDeclaration) {
39610+
const reference = isComputedPropertyName(propName)
39611+
? factory.createElementAccessExpression(factory.createThis(), propName.expression)
39612+
: factory.createPropertyAccessExpression(factory.createThis(), propName);
3957039613
setParent(reference.expression, reference);
3957139614
setParent(reference, constructor);
3957239615
reference.flowNode = constructor.returnFlowNode;

src/compiler/commandLineParser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2755,7 +2755,7 @@ namespace ts {
27552755
function getPropFromRaw<T>(prop: "files" | "include" | "exclude" | "references", validateElement: (value: unknown) => boolean, elementTypeName: string): PropOfRaw<T> {
27562756
if (hasProperty(raw, prop) && !isNullOrUndefined(raw[prop])) {
27572757
if (isArray(raw[prop])) {
2758-
const result = raw[prop];
2758+
const result = raw[prop] as T[];
27592759
if (!sourceFile && !every(result, validateElement)) {
27602760
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, prop, elementTypeName));
27612761
}

tests/baselines/reference/incrementOnNullAssertion.types

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,21 @@ if (foo[x] === undefined) {
2727
}
2828
else {
2929
let nu = foo[x]
30-
>nu : number | undefined
31-
>foo[x] : number | undefined
30+
>nu : number
31+
>foo[x] : number
3232
>foo : Dictionary<number>
3333
>x : "bar"
3434

3535
let n = foo[x]
36-
>n : number | undefined
37-
>foo[x] : number | undefined
36+
>n : number
37+
>foo[x] : number
3838
>foo : Dictionary<number>
3939
>x : "bar"
4040

4141
foo[x]!++
4242
>foo[x]!++ : number
4343
>foo[x]! : number
44-
>foo[x] : number | undefined
44+
>foo[x] : number
4545
>foo : Dictionary<number>
4646
>x : "bar"
4747
}

tests/baselines/reference/strictPropertyInitialization.errors.txt

+15
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,19 @@ tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitial
164164
this.#b = someValue();
165165
}
166166
}
167+
168+
const a = 'a';
169+
const b = Symbol();
170+
171+
class C12 {
172+
[a]: number;
173+
[b]: number;
174+
['c']: number;
175+
176+
constructor() {
177+
this[a] = 1;
178+
this[b] = 1;
179+
this['c'] = 1;
180+
}
181+
}
167182

tests/baselines/reference/strictPropertyInitialization.js

+32
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,21 @@ class C11 {
132132
this.#b = someValue();
133133
}
134134
}
135+
136+
const a = 'a';
137+
const b = Symbol();
138+
139+
class C12 {
140+
[a]: number;
141+
[b]: number;
142+
['c']: number;
143+
144+
constructor() {
145+
this[a] = 1;
146+
this[b] = 1;
147+
this['c'] = 1;
148+
}
149+
}
135150

136151

137152
//// [strictPropertyInitialization.js]
@@ -235,6 +250,15 @@ class C11 {
235250
}
236251
}
237252
_C11_b = new WeakMap();
253+
const a = 'a';
254+
const b = Symbol();
255+
class C12 {
256+
constructor() {
257+
this[a] = 1;
258+
this[b] = 1;
259+
this['c'] = 1;
260+
}
261+
}
238262

239263

240264
//// [strictPropertyInitialization.d.ts]
@@ -303,3 +327,11 @@ declare class C11 {
303327
a: number;
304328
constructor();
305329
}
330+
declare const a = "a";
331+
declare const b: unique symbol;
332+
declare class C12 {
333+
[a]: number;
334+
[b]: number;
335+
['c']: number;
336+
constructor();
337+
}

tests/baselines/reference/strictPropertyInitialization.symbols

+37
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,40 @@ class C11 {
311311
}
312312
}
313313

314+
const a = 'a';
315+
>a : Symbol(a, Decl(strictPropertyInitialization.ts, 134, 5))
316+
317+
const b = Symbol();
318+
>b : Symbol(b, Decl(strictPropertyInitialization.ts, 135, 5))
319+
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
320+
321+
class C12 {
322+
>C12 : Symbol(C12, Decl(strictPropertyInitialization.ts, 135, 19))
323+
324+
[a]: number;
325+
>[a] : Symbol(C12[a], Decl(strictPropertyInitialization.ts, 137, 11))
326+
>a : Symbol(a, Decl(strictPropertyInitialization.ts, 134, 5))
327+
328+
[b]: number;
329+
>[b] : Symbol(C12[b], Decl(strictPropertyInitialization.ts, 138, 16))
330+
>b : Symbol(b, Decl(strictPropertyInitialization.ts, 135, 5))
331+
332+
['c']: number;
333+
>['c'] : Symbol(C12['c'], Decl(strictPropertyInitialization.ts, 139, 16))
334+
>'c' : Symbol(C12['c'], Decl(strictPropertyInitialization.ts, 139, 16))
335+
336+
constructor() {
337+
this[a] = 1;
338+
>this : Symbol(C12, Decl(strictPropertyInitialization.ts, 135, 19))
339+
>a : Symbol(a, Decl(strictPropertyInitialization.ts, 134, 5))
340+
341+
this[b] = 1;
342+
>this : Symbol(C12, Decl(strictPropertyInitialization.ts, 135, 19))
343+
>b : Symbol(b, Decl(strictPropertyInitialization.ts, 135, 5))
344+
345+
this['c'] = 1;
346+
>this : Symbol(C12, Decl(strictPropertyInitialization.ts, 135, 19))
347+
>'c' : Symbol(C12['c'], Decl(strictPropertyInitialization.ts, 139, 16))
348+
}
349+
}
350+

tests/baselines/reference/strictPropertyInitialization.types

+48
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,51 @@ class C11 {
347347
}
348348
}
349349

350+
const a = 'a';
351+
>a : "a"
352+
>'a' : "a"
353+
354+
const b = Symbol();
355+
>b : unique symbol
356+
>Symbol() : unique symbol
357+
>Symbol : SymbolConstructor
358+
359+
class C12 {
360+
>C12 : C12
361+
362+
[a]: number;
363+
>[a] : number
364+
>a : "a"
365+
366+
[b]: number;
367+
>[b] : number
368+
>b : unique symbol
369+
370+
['c']: number;
371+
>['c'] : number
372+
>'c' : "c"
373+
374+
constructor() {
375+
this[a] = 1;
376+
>this[a] = 1 : 1
377+
>this[a] : number
378+
>this : this
379+
>a : "a"
380+
>1 : 1
381+
382+
this[b] = 1;
383+
>this[b] = 1 : 1
384+
>this[b] : number
385+
>this : this
386+
>b : unique symbol
387+
>1 : 1
388+
389+
this['c'] = 1;
390+
>this['c'] = 1 : 1
391+
>this['c'] : number
392+
>this : this
393+
>'c' : "c"
394+
>1 : 1
395+
}
396+
}
397+

tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty1.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//// [typeGuardNarrowsIndexedAccessOfKnownProperty.ts]
1+
//// [typeGuardNarrowsIndexedAccessOfKnownProperty1.ts]
22
interface Square {
33
["dash-ok"]: "square";
44
["square-size"]: number;
@@ -80,7 +80,7 @@ export function g(pair: [number, string?]): string {
8080
}
8181

8282

83-
//// [typeGuardNarrowsIndexedAccessOfKnownProperty.js]
83+
//// [typeGuardNarrowsIndexedAccessOfKnownProperty1.js]
8484
"use strict";
8585
exports.__esModule = true;
8686
exports.g = void 0;

0 commit comments

Comments
 (0)