Skip to content

Commit 208148d

Browse files
committed
Fix crash in getTextOfPropertyName
1 parent d53619a commit 208148d

File tree

6 files changed

+243
-16
lines changed

6 files changed

+243
-16
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6447,6 +6447,13 @@ namespace ts {
64476447
return isDynamicName(node) && !isLateBindableName(node);
64486448
}
64496449

6450+
/**
6451+
* Gets the late-bound name for a computed property name.
6452+
*/
6453+
function getLateBoundName(node: LateBoundName) {
6454+
return getLateBoundNameFromType(checkComputedPropertyName(node));
6455+
}
6456+
64506457
/**
64516458
* Gets the symbolic name for a late-bound member from its type.
64526459
*/
@@ -7354,7 +7361,7 @@ namespace ts {
73547361
function isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean {
73557362
const list = obj.properties as NodeArray<ObjectLiteralElementLike | JsxAttributeLike>;
73567363
return list.some(property => {
7357-
const name = property.name && getTextOfPropertyName(property.name);
7364+
const name = property.name && !isComputedNonLiteralName(property.name) ? getTextOfPropertyName(property.name) : undefined;
73587365
const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name);
73597366
return !!expected && isLiteralType(expected) && !isTypeIdenticalTo(getTypeOfNode(property), expected);
73607367
});
@@ -15059,7 +15066,10 @@ namespace ts {
1505915066
}
1506015067

1506115068
function getTypeOfDestructuredProperty(type: Type, name: PropertyName) {
15062-
const text = getTextOfPropertyName(name);
15069+
const text = !isComputedNonLiteralName(name) ? getTextOfPropertyName(name) :
15070+
isLateBindableName(name) ? getLateBoundName(name) :
15071+
undefined;
15072+
if (text === undefined) return errorType;
1506315073
return getConstraintForLocation(getTypeOfPropertyOfType(type, text), name) ||
1506415074
isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) ||
1506515075
getIndexTypeOfType(type, IndexKind.String) ||
@@ -17191,11 +17201,9 @@ namespace ts {
1719117201
const parentDeclaration = declaration.parent.parent;
1719217202
const name = declaration.propertyName || declaration.name;
1719317203
const parentType = getContextualTypeForVariableLikeDeclaration(parentDeclaration);
17194-
if (parentType && !isBindingPattern(name)) {
17204+
if (parentType && !isBindingPattern(name) && !isComputedNonLiteralName(name)) {
1719517205
const text = getTextOfPropertyName(name);
17196-
if (text !== undefined) {
17197-
return getTypeOfPropertyOfType(parentType, text);
17198-
}
17206+
return getTypeOfPropertyOfType(parentType, text);
1719917207
}
1720017208
}
1720117209

@@ -22201,8 +22209,8 @@ namespace ts {
2220122209
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: NodeArray<ObjectLiteralElementLike>, rightIsThis = false) {
2220222210
if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) {
2220322211
const name = property.name;
22204-
const text = getTextOfPropertyName(name);
22205-
if (text) {
22212+
if (!isComputedNonLiteralName(name)) {
22213+
const text = getTextOfPropertyName(name);
2220622214
const prop = getPropertyOfType(objectLiteralType, text);
2220722215
if (prop) {
2220822216
markPropertyAsReferenced(prop, property, rightIsThis);
@@ -25524,14 +25532,12 @@ namespace ts {
2552425532
const parent = node.parent.parent;
2552525533
const parentType = getTypeForBindingElementParent(parent);
2552625534
const name = node.propertyName || node.name;
25527-
if (!isBindingPattern(name)) {
25535+
if (!isBindingPattern(name) && !isComputedNonLiteralName(name)) {
2552825536
const nameText = getTextOfPropertyName(name);
25529-
if (nameText) {
25530-
const property = getPropertyOfType(parentType!, nameText); // TODO: GH#18217
25531-
if (property) {
25532-
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
25533-
checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
25534-
}
25537+
const property = getPropertyOfType(parentType!, nameText); // TODO: GH#18217
25538+
if (property) {
25539+
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
25540+
checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
2553525541
}
2553625542
}
2553725543
}

src/compiler/utilities.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,8 @@ namespace ts {
778778
case SyntaxKind.NoSubstitutionTemplateLiteral:
779779
return escapeLeadingUnderscores(name.text);
780780
case SyntaxKind.ComputedPropertyName:
781-
return isStringOrNumericLiteralLike(name.expression) ? escapeLeadingUnderscores(name.expression.text) : undefined!; // TODO: GH#18217 Almost all uses of this assume the result to be defined!
781+
if (isStringOrNumericLiteralLike(name.expression)) return escapeLeadingUnderscores(name.expression.text);
782+
return Debug.fail("Text of property name cannot be read from non-literal-valued ComputedPropertyNames");
782783
default:
783784
return Debug.assertNever(name);
784785
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//// [crashInGetTextOfComputedPropertyName.ts]
2+
// https://github.com/Microsoft/TypeScript/issues/29006
3+
export interface A { type: 'a' }
4+
export interface B { type: 'b' }
5+
export type AB = A | B
6+
7+
const itemId = 'some-id'
8+
9+
// --- test on first level ---
10+
const items: { [id: string]: AB } = {}
11+
const { [itemId]: itemOk1 } = items
12+
typeof itemOk1 // pass
13+
14+
// --- test on second level ---
15+
interface ObjWithItems {
16+
items: {[s: string]: AB}
17+
}
18+
const objWithItems: ObjWithItems = { items: {}}
19+
20+
const itemOk2 = objWithItems.items[itemId]
21+
typeof itemOk2 // pass
22+
23+
const {
24+
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
25+
} = objWithItems
26+
27+
// in order to re-produce the error, uncomment next line:
28+
typeof itemWithTSError // :(
29+
30+
// will result in:
31+
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
32+
33+
//// [crashInGetTextOfComputedPropertyName.js]
34+
"use strict";
35+
exports.__esModule = true;
36+
var itemId = 'some-id';
37+
// --- test on first level ---
38+
var items = {};
39+
var _a = itemId, itemOk1 = items[_a];
40+
typeof itemOk1; // pass
41+
var objWithItems = { items: {} };
42+
var itemOk2 = objWithItems.items[itemId];
43+
typeof itemOk2; // pass
44+
var _b = objWithItems.items /*happens when default value is provided*/, _c = itemId, itemWithTSError = (_b === void 0 ? {} /*happens when default value is provided*/ : _b)[_c];
45+
// in order to re-produce the error, uncomment next line:
46+
typeof itemWithTSError; // :(
47+
// will result in:
48+
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
=== tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts ===
2+
// https://github.com/Microsoft/TypeScript/issues/29006
3+
export interface A { type: 'a' }
4+
>A : Symbol(A, Decl(crashInGetTextOfComputedPropertyName.ts, 0, 0))
5+
>type : Symbol(A.type, Decl(crashInGetTextOfComputedPropertyName.ts, 1, 20))
6+
7+
export interface B { type: 'b' }
8+
>B : Symbol(B, Decl(crashInGetTextOfComputedPropertyName.ts, 1, 32))
9+
>type : Symbol(B.type, Decl(crashInGetTextOfComputedPropertyName.ts, 2, 20))
10+
11+
export type AB = A | B
12+
>AB : Symbol(AB, Decl(crashInGetTextOfComputedPropertyName.ts, 2, 32))
13+
>A : Symbol(A, Decl(crashInGetTextOfComputedPropertyName.ts, 0, 0))
14+
>B : Symbol(B, Decl(crashInGetTextOfComputedPropertyName.ts, 1, 32))
15+
16+
const itemId = 'some-id'
17+
>itemId : Symbol(itemId, Decl(crashInGetTextOfComputedPropertyName.ts, 5, 5))
18+
19+
// --- test on first level ---
20+
const items: { [id: string]: AB } = {}
21+
>items : Symbol(items, Decl(crashInGetTextOfComputedPropertyName.ts, 8, 5))
22+
>id : Symbol(id, Decl(crashInGetTextOfComputedPropertyName.ts, 8, 16))
23+
>AB : Symbol(AB, Decl(crashInGetTextOfComputedPropertyName.ts, 2, 32))
24+
25+
const { [itemId]: itemOk1 } = items
26+
>itemId : Symbol(itemId, Decl(crashInGetTextOfComputedPropertyName.ts, 5, 5))
27+
>itemOk1 : Symbol(itemOk1, Decl(crashInGetTextOfComputedPropertyName.ts, 9, 7))
28+
>items : Symbol(items, Decl(crashInGetTextOfComputedPropertyName.ts, 8, 5))
29+
30+
typeof itemOk1 // pass
31+
>itemOk1 : Symbol(itemOk1, Decl(crashInGetTextOfComputedPropertyName.ts, 9, 7))
32+
33+
// --- test on second level ---
34+
interface ObjWithItems {
35+
>ObjWithItems : Symbol(ObjWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 10, 14))
36+
37+
items: {[s: string]: AB}
38+
>items : Symbol(ObjWithItems.items, Decl(crashInGetTextOfComputedPropertyName.ts, 13, 24))
39+
>s : Symbol(s, Decl(crashInGetTextOfComputedPropertyName.ts, 14, 13))
40+
>AB : Symbol(AB, Decl(crashInGetTextOfComputedPropertyName.ts, 2, 32))
41+
}
42+
const objWithItems: ObjWithItems = { items: {}}
43+
>objWithItems : Symbol(objWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 16, 5))
44+
>ObjWithItems : Symbol(ObjWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 10, 14))
45+
>items : Symbol(items, Decl(crashInGetTextOfComputedPropertyName.ts, 16, 36))
46+
47+
const itemOk2 = objWithItems.items[itemId]
48+
>itemOk2 : Symbol(itemOk2, Decl(crashInGetTextOfComputedPropertyName.ts, 18, 5))
49+
>objWithItems.items : Symbol(ObjWithItems.items, Decl(crashInGetTextOfComputedPropertyName.ts, 13, 24))
50+
>objWithItems : Symbol(objWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 16, 5))
51+
>items : Symbol(ObjWithItems.items, Decl(crashInGetTextOfComputedPropertyName.ts, 13, 24))
52+
>itemId : Symbol(itemId, Decl(crashInGetTextOfComputedPropertyName.ts, 5, 5))
53+
54+
typeof itemOk2 // pass
55+
>itemOk2 : Symbol(itemOk2, Decl(crashInGetTextOfComputedPropertyName.ts, 18, 5))
56+
57+
const {
58+
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
59+
>items : Symbol(ObjWithItems.items, Decl(crashInGetTextOfComputedPropertyName.ts, 13, 24))
60+
>itemId : Symbol(itemId, Decl(crashInGetTextOfComputedPropertyName.ts, 5, 5))
61+
>itemWithTSError : Symbol(itemWithTSError, Decl(crashInGetTextOfComputedPropertyName.ts, 22, 12))
62+
63+
} = objWithItems
64+
>objWithItems : Symbol(objWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 16, 5))
65+
66+
// in order to re-produce the error, uncomment next line:
67+
typeof itemWithTSError // :(
68+
>itemWithTSError : Symbol(itemWithTSError, Decl(crashInGetTextOfComputedPropertyName.ts, 22, 12))
69+
70+
// will result in:
71+
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
=== tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts ===
2+
// https://github.com/Microsoft/TypeScript/issues/29006
3+
export interface A { type: 'a' }
4+
>type : "a"
5+
6+
export interface B { type: 'b' }
7+
>type : "b"
8+
9+
export type AB = A | B
10+
>AB : AB
11+
12+
const itemId = 'some-id'
13+
>itemId : "some-id"
14+
>'some-id' : "some-id"
15+
16+
// --- test on first level ---
17+
const items: { [id: string]: AB } = {}
18+
>items : { [id: string]: AB; }
19+
>id : string
20+
>{} : {}
21+
22+
const { [itemId]: itemOk1 } = items
23+
>itemId : "some-id"
24+
>itemOk1 : AB
25+
>items : { [id: string]: AB; }
26+
27+
typeof itemOk1 // pass
28+
>typeof itemOk1 : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
29+
>itemOk1 : AB
30+
31+
// --- test on second level ---
32+
interface ObjWithItems {
33+
items: {[s: string]: AB}
34+
>items : { [s: string]: AB; }
35+
>s : string
36+
}
37+
const objWithItems: ObjWithItems = { items: {}}
38+
>objWithItems : ObjWithItems
39+
>{ items: {}} : { items: {}; }
40+
>items : {}
41+
>{} : {}
42+
43+
const itemOk2 = objWithItems.items[itemId]
44+
>itemOk2 : AB
45+
>objWithItems.items[itemId] : AB
46+
>objWithItems.items : { [s: string]: AB; }
47+
>objWithItems : ObjWithItems
48+
>items : { [s: string]: AB; }
49+
>itemId : "some-id"
50+
51+
typeof itemOk2 // pass
52+
>typeof itemOk2 : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
53+
>itemOk2 : AB
54+
55+
const {
56+
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
57+
>items : any
58+
>itemId : "some-id"
59+
>itemWithTSError : AB
60+
>{} : {}
61+
62+
} = objWithItems
63+
>objWithItems : ObjWithItems
64+
65+
// in order to re-produce the error, uncomment next line:
66+
typeof itemWithTSError // :(
67+
>typeof itemWithTSError : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
68+
>itemWithTSError : AB
69+
70+
// will result in:
71+
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// https://github.com/Microsoft/TypeScript/issues/29006
2+
export interface A { type: 'a' }
3+
export interface B { type: 'b' }
4+
export type AB = A | B
5+
6+
const itemId = 'some-id'
7+
8+
// --- test on first level ---
9+
const items: { [id: string]: AB } = {}
10+
const { [itemId]: itemOk1 } = items
11+
typeof itemOk1 // pass
12+
13+
// --- test on second level ---
14+
interface ObjWithItems {
15+
items: {[s: string]: AB}
16+
}
17+
const objWithItems: ObjWithItems = { items: {}}
18+
19+
const itemOk2 = objWithItems.items[itemId]
20+
typeof itemOk2 // pass
21+
22+
const {
23+
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
24+
} = objWithItems
25+
26+
// in order to re-produce the error, uncomment next line:
27+
typeof itemWithTSError // :(
28+
29+
// will result in:
30+
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined

0 commit comments

Comments
 (0)