Skip to content

Commit 591ce0f

Browse files
committed
fix(40617): handle uninitialized class member with computed key
1 parent 7f022c5 commit 591ce0f

File tree

41 files changed

+1256
-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.

41 files changed

+1256
-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
@@ -22773,9 +22773,10 @@ namespace ts {
2277322773
return isMatchingReference((source as NonNullExpression | ParenthesizedExpression).expression, target);
2277422774
case SyntaxKind.PropertyAccessExpression:
2277522775
case SyntaxKind.ElementAccessExpression:
22776-
return isAccessExpression(target) &&
22777-
getAccessedPropertyName(source as AccessExpression) === getAccessedPropertyName(target) &&
22778-
isMatchingReference((source as AccessExpression).expression, target.expression);
22776+
const sourcePropertyName = getAccessedPropertyName(source as AccessExpression);
22777+
const targetPropertyName = isAccessExpression(target) ? getAccessedPropertyName(target) : undefined;
22778+
return sourcePropertyName !== undefined && targetPropertyName !== undefined && targetPropertyName === sourcePropertyName &&
22779+
isMatchingReference((source as AccessExpression).expression, (target as AccessExpression).expression);
2277922780
case SyntaxKind.QualifiedName:
2278022781
return isAccessExpression(target) &&
2278122782
(source as QualifiedName).right.escapedText === getAccessedPropertyName(target) &&
@@ -22787,12 +22788,52 @@ namespace ts {
2278722788
}
2278822789

2278922790
function getAccessedPropertyName(access: AccessExpression | BindingElement | ParameterDeclaration): __String | undefined {
22790-
let propertyName;
22791-
return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
22792-
access.kind === SyntaxKind.ElementAccessExpression && isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
22793-
access.kind === SyntaxKind.BindingElement && (propertyName = getDestructuringPropertyName(access)) ? escapeLeadingUnderscores(propertyName) :
22794-
access.kind === SyntaxKind.Parameter ? ("" + access.parent.parameters.indexOf(access)) as __String :
22795-
undefined;
22791+
if (isPropertyAccessExpression(access)) {
22792+
return access.name.escapedText;
22793+
}
22794+
if (isElementAccessExpression(access)) {
22795+
return tryGetElementAccessExpressionName(access);
22796+
}
22797+
if (isBindingElement(access)) {
22798+
const name = getDestructuringPropertyName(access);
22799+
return name ? escapeLeadingUnderscores(name) : undefined;
22800+
}
22801+
if (isParameter(access)) {
22802+
return ("" + access.parent.parameters.indexOf(access)) as __String;
22803+
}
22804+
return undefined;
22805+
}
22806+
22807+
function tryGetNameFromType(type: Type) {
22808+
return type.flags & TypeFlags.UniqueESSymbol ? (type as UniqueESSymbolType).escapedName :
22809+
type.flags & TypeFlags.StringOrNumberLiteral ? escapeLeadingUnderscores("" + (type as StringLiteralType | NumberLiteralType).value) : undefined;
22810+
}
22811+
22812+
function tryGetElementAccessExpressionName(node: ElementAccessExpression) {
22813+
if (isStringOrNumericLiteralLike(node.argumentExpression)) {
22814+
return escapeLeadingUnderscores(node.argumentExpression.text);
22815+
}
22816+
if (isEntityNameExpression(node.argumentExpression)) {
22817+
const symbol = resolveEntityName(node.argumentExpression, SymbolFlags.Value, /*ignoreErrors*/ true);
22818+
if (!symbol || !isConstVariable(symbol)) return undefined;
22819+
22820+
const declaration = symbol.valueDeclaration;
22821+
if (declaration === undefined) return undefined;
22822+
22823+
const type = tryGetTypeFromEffectiveTypeNode(declaration);
22824+
if (type) {
22825+
const name = tryGetNameFromType(type);
22826+
if (name !== undefined) {
22827+
return name;
22828+
}
22829+
}
22830+
22831+
if (hasOnlyExpressionInitializer(declaration)) {
22832+
const initializer = getEffectiveInitializer(declaration);
22833+
return initializer && tryGetNameFromType(getTypeOfExpression(initializer));
22834+
}
22835+
}
22836+
return undefined;
2279622837
}
2279722838

2279822839
function containsMatchingReference(source: Node, target: Node) {
@@ -39310,7 +39351,7 @@ namespace ts {
3931039351
}
3931139352
if (!isStatic(member) && isPropertyWithoutInitializer(member)) {
3931239353
const propName = (member as PropertyDeclaration).name;
39313-
if (isIdentifier(propName) || isPrivateIdentifier(propName)) {
39354+
if (isIdentifier(propName) || isPrivateIdentifier(propName) || isComputedPropertyName(propName)) {
3931439355
const type = getTypeOfSymbol(getSymbolOfNode(member));
3931539356
if (!(type.flags & TypeFlags.AnyOrUnknown || getFalsyFlags(type) & TypeFlags.Undefined)) {
3931639357
if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) {
@@ -39346,8 +39387,10 @@ namespace ts {
3934639387
return false;
3934739388
}
3934839389

39349-
function isPropertyInitializedInConstructor(propName: Identifier | PrivateIdentifier, propType: Type, constructor: ConstructorDeclaration) {
39350-
const reference = factory.createPropertyAccessExpression(factory.createThis(), propName);
39390+
function isPropertyInitializedInConstructor(propName: Identifier | PrivateIdentifier | ComputedPropertyName, propType: Type, constructor: ConstructorDeclaration) {
39391+
const reference = isComputedPropertyName(propName)
39392+
? factory.createElementAccessExpression(factory.createThis(), propName.expression)
39393+
: factory.createPropertyAccessExpression(factory.createThis(), propName);
3935139394
setParent(reference.expression, reference);
3935239395
setParent(reference, constructor);
3935339396
reference.flowNode = constructor.returnFlowNode;

src/compiler/commandLineParser.ts

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

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)