Fix crash in getTextOfPropertyName
This commit is contained in:
parent
d53619a30d
commit
208148d05c
|
@ -6447,6 +6447,13 @@ namespace ts {
|
|||
return isDynamicName(node) && !isLateBindableName(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the late-bound name for a computed property name.
|
||||
*/
|
||||
function getLateBoundName(node: LateBoundName) {
|
||||
return getLateBoundNameFromType(checkComputedPropertyName(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the symbolic name for a late-bound member from its type.
|
||||
*/
|
||||
|
@ -7354,7 +7361,7 @@ namespace ts {
|
|||
function isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean {
|
||||
const list = obj.properties as NodeArray<ObjectLiteralElementLike | JsxAttributeLike>;
|
||||
return list.some(property => {
|
||||
const name = property.name && getTextOfPropertyName(property.name);
|
||||
const name = property.name && !isComputedNonLiteralName(property.name) ? getTextOfPropertyName(property.name) : undefined;
|
||||
const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name);
|
||||
return !!expected && isLiteralType(expected) && !isTypeIdenticalTo(getTypeOfNode(property), expected);
|
||||
});
|
||||
|
@ -15059,7 +15066,10 @@ namespace ts {
|
|||
}
|
||||
|
||||
function getTypeOfDestructuredProperty(type: Type, name: PropertyName) {
|
||||
const text = getTextOfPropertyName(name);
|
||||
const text = !isComputedNonLiteralName(name) ? getTextOfPropertyName(name) :
|
||||
isLateBindableName(name) ? getLateBoundName(name) :
|
||||
undefined;
|
||||
if (text === undefined) return errorType;
|
||||
return getConstraintForLocation(getTypeOfPropertyOfType(type, text), name) ||
|
||||
isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) ||
|
||||
getIndexTypeOfType(type, IndexKind.String) ||
|
||||
|
@ -17191,11 +17201,9 @@ namespace ts {
|
|||
const parentDeclaration = declaration.parent.parent;
|
||||
const name = declaration.propertyName || declaration.name;
|
||||
const parentType = getContextualTypeForVariableLikeDeclaration(parentDeclaration);
|
||||
if (parentType && !isBindingPattern(name)) {
|
||||
if (parentType && !isBindingPattern(name) && !isComputedNonLiteralName(name)) {
|
||||
const text = getTextOfPropertyName(name);
|
||||
if (text !== undefined) {
|
||||
return getTypeOfPropertyOfType(parentType, text);
|
||||
}
|
||||
return getTypeOfPropertyOfType(parentType, text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22201,8 +22209,8 @@ namespace ts {
|
|||
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: NodeArray<ObjectLiteralElementLike>, rightIsThis = false) {
|
||||
if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const name = property.name;
|
||||
const text = getTextOfPropertyName(name);
|
||||
if (text) {
|
||||
if (!isComputedNonLiteralName(name)) {
|
||||
const text = getTextOfPropertyName(name);
|
||||
const prop = getPropertyOfType(objectLiteralType, text);
|
||||
if (prop) {
|
||||
markPropertyAsReferenced(prop, property, rightIsThis);
|
||||
|
@ -25524,14 +25532,12 @@ namespace ts {
|
|||
const parent = node.parent.parent;
|
||||
const parentType = getTypeForBindingElementParent(parent);
|
||||
const name = node.propertyName || node.name;
|
||||
if (!isBindingPattern(name)) {
|
||||
if (!isBindingPattern(name) && !isComputedNonLiteralName(name)) {
|
||||
const nameText = getTextOfPropertyName(name);
|
||||
if (nameText) {
|
||||
const property = getPropertyOfType(parentType!, nameText); // TODO: GH#18217
|
||||
if (property) {
|
||||
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
|
||||
checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
|
||||
}
|
||||
const property = getPropertyOfType(parentType!, nameText); // TODO: GH#18217
|
||||
if (property) {
|
||||
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
|
||||
checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -778,7 +778,8 @@ namespace ts {
|
|||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
return escapeLeadingUnderscores(name.text);
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
return isStringOrNumericLiteralLike(name.expression) ? escapeLeadingUnderscores(name.expression.text) : undefined!; // TODO: GH#18217 Almost all uses of this assume the result to be defined!
|
||||
if (isStringOrNumericLiteralLike(name.expression)) return escapeLeadingUnderscores(name.expression.text);
|
||||
return Debug.fail("Text of property name cannot be read from non-literal-valued ComputedPropertyNames");
|
||||
default:
|
||||
return Debug.assertNever(name);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
//// [crashInGetTextOfComputedPropertyName.ts]
|
||||
// https://github.com/Microsoft/TypeScript/issues/29006
|
||||
export interface A { type: 'a' }
|
||||
export interface B { type: 'b' }
|
||||
export type AB = A | B
|
||||
|
||||
const itemId = 'some-id'
|
||||
|
||||
// --- test on first level ---
|
||||
const items: { [id: string]: AB } = {}
|
||||
const { [itemId]: itemOk1 } = items
|
||||
typeof itemOk1 // pass
|
||||
|
||||
// --- test on second level ---
|
||||
interface ObjWithItems {
|
||||
items: {[s: string]: AB}
|
||||
}
|
||||
const objWithItems: ObjWithItems = { items: {}}
|
||||
|
||||
const itemOk2 = objWithItems.items[itemId]
|
||||
typeof itemOk2 // pass
|
||||
|
||||
const {
|
||||
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
|
||||
} = objWithItems
|
||||
|
||||
// in order to re-produce the error, uncomment next line:
|
||||
typeof itemWithTSError // :(
|
||||
|
||||
// will result in:
|
||||
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
|
||||
|
||||
//// [crashInGetTextOfComputedPropertyName.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var itemId = 'some-id';
|
||||
// --- test on first level ---
|
||||
var items = {};
|
||||
var _a = itemId, itemOk1 = items[_a];
|
||||
typeof itemOk1; // pass
|
||||
var objWithItems = { items: {} };
|
||||
var itemOk2 = objWithItems.items[itemId];
|
||||
typeof itemOk2; // pass
|
||||
var _b = objWithItems.items /*happens when default value is provided*/, _c = itemId, itemWithTSError = (_b === void 0 ? {} /*happens when default value is provided*/ : _b)[_c];
|
||||
// in order to re-produce the error, uncomment next line:
|
||||
typeof itemWithTSError; // :(
|
||||
// will result in:
|
||||
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
|
|
@ -0,0 +1,71 @@
|
|||
=== tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts ===
|
||||
// https://github.com/Microsoft/TypeScript/issues/29006
|
||||
export interface A { type: 'a' }
|
||||
>A : Symbol(A, Decl(crashInGetTextOfComputedPropertyName.ts, 0, 0))
|
||||
>type : Symbol(A.type, Decl(crashInGetTextOfComputedPropertyName.ts, 1, 20))
|
||||
|
||||
export interface B { type: 'b' }
|
||||
>B : Symbol(B, Decl(crashInGetTextOfComputedPropertyName.ts, 1, 32))
|
||||
>type : Symbol(B.type, Decl(crashInGetTextOfComputedPropertyName.ts, 2, 20))
|
||||
|
||||
export type AB = A | B
|
||||
>AB : Symbol(AB, Decl(crashInGetTextOfComputedPropertyName.ts, 2, 32))
|
||||
>A : Symbol(A, Decl(crashInGetTextOfComputedPropertyName.ts, 0, 0))
|
||||
>B : Symbol(B, Decl(crashInGetTextOfComputedPropertyName.ts, 1, 32))
|
||||
|
||||
const itemId = 'some-id'
|
||||
>itemId : Symbol(itemId, Decl(crashInGetTextOfComputedPropertyName.ts, 5, 5))
|
||||
|
||||
// --- test on first level ---
|
||||
const items: { [id: string]: AB } = {}
|
||||
>items : Symbol(items, Decl(crashInGetTextOfComputedPropertyName.ts, 8, 5))
|
||||
>id : Symbol(id, Decl(crashInGetTextOfComputedPropertyName.ts, 8, 16))
|
||||
>AB : Symbol(AB, Decl(crashInGetTextOfComputedPropertyName.ts, 2, 32))
|
||||
|
||||
const { [itemId]: itemOk1 } = items
|
||||
>itemId : Symbol(itemId, Decl(crashInGetTextOfComputedPropertyName.ts, 5, 5))
|
||||
>itemOk1 : Symbol(itemOk1, Decl(crashInGetTextOfComputedPropertyName.ts, 9, 7))
|
||||
>items : Symbol(items, Decl(crashInGetTextOfComputedPropertyName.ts, 8, 5))
|
||||
|
||||
typeof itemOk1 // pass
|
||||
>itemOk1 : Symbol(itemOk1, Decl(crashInGetTextOfComputedPropertyName.ts, 9, 7))
|
||||
|
||||
// --- test on second level ---
|
||||
interface ObjWithItems {
|
||||
>ObjWithItems : Symbol(ObjWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 10, 14))
|
||||
|
||||
items: {[s: string]: AB}
|
||||
>items : Symbol(ObjWithItems.items, Decl(crashInGetTextOfComputedPropertyName.ts, 13, 24))
|
||||
>s : Symbol(s, Decl(crashInGetTextOfComputedPropertyName.ts, 14, 13))
|
||||
>AB : Symbol(AB, Decl(crashInGetTextOfComputedPropertyName.ts, 2, 32))
|
||||
}
|
||||
const objWithItems: ObjWithItems = { items: {}}
|
||||
>objWithItems : Symbol(objWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 16, 5))
|
||||
>ObjWithItems : Symbol(ObjWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 10, 14))
|
||||
>items : Symbol(items, Decl(crashInGetTextOfComputedPropertyName.ts, 16, 36))
|
||||
|
||||
const itemOk2 = objWithItems.items[itemId]
|
||||
>itemOk2 : Symbol(itemOk2, Decl(crashInGetTextOfComputedPropertyName.ts, 18, 5))
|
||||
>objWithItems.items : Symbol(ObjWithItems.items, Decl(crashInGetTextOfComputedPropertyName.ts, 13, 24))
|
||||
>objWithItems : Symbol(objWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 16, 5))
|
||||
>items : Symbol(ObjWithItems.items, Decl(crashInGetTextOfComputedPropertyName.ts, 13, 24))
|
||||
>itemId : Symbol(itemId, Decl(crashInGetTextOfComputedPropertyName.ts, 5, 5))
|
||||
|
||||
typeof itemOk2 // pass
|
||||
>itemOk2 : Symbol(itemOk2, Decl(crashInGetTextOfComputedPropertyName.ts, 18, 5))
|
||||
|
||||
const {
|
||||
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
|
||||
>items : Symbol(ObjWithItems.items, Decl(crashInGetTextOfComputedPropertyName.ts, 13, 24))
|
||||
>itemId : Symbol(itemId, Decl(crashInGetTextOfComputedPropertyName.ts, 5, 5))
|
||||
>itemWithTSError : Symbol(itemWithTSError, Decl(crashInGetTextOfComputedPropertyName.ts, 22, 12))
|
||||
|
||||
} = objWithItems
|
||||
>objWithItems : Symbol(objWithItems, Decl(crashInGetTextOfComputedPropertyName.ts, 16, 5))
|
||||
|
||||
// in order to re-produce the error, uncomment next line:
|
||||
typeof itemWithTSError // :(
|
||||
>itemWithTSError : Symbol(itemWithTSError, Decl(crashInGetTextOfComputedPropertyName.ts, 22, 12))
|
||||
|
||||
// will result in:
|
||||
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
|
|
@ -0,0 +1,71 @@
|
|||
=== tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts ===
|
||||
// https://github.com/Microsoft/TypeScript/issues/29006
|
||||
export interface A { type: 'a' }
|
||||
>type : "a"
|
||||
|
||||
export interface B { type: 'b' }
|
||||
>type : "b"
|
||||
|
||||
export type AB = A | B
|
||||
>AB : AB
|
||||
|
||||
const itemId = 'some-id'
|
||||
>itemId : "some-id"
|
||||
>'some-id' : "some-id"
|
||||
|
||||
// --- test on first level ---
|
||||
const items: { [id: string]: AB } = {}
|
||||
>items : { [id: string]: AB; }
|
||||
>id : string
|
||||
>{} : {}
|
||||
|
||||
const { [itemId]: itemOk1 } = items
|
||||
>itemId : "some-id"
|
||||
>itemOk1 : AB
|
||||
>items : { [id: string]: AB; }
|
||||
|
||||
typeof itemOk1 // pass
|
||||
>typeof itemOk1 : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>itemOk1 : AB
|
||||
|
||||
// --- test on second level ---
|
||||
interface ObjWithItems {
|
||||
items: {[s: string]: AB}
|
||||
>items : { [s: string]: AB; }
|
||||
>s : string
|
||||
}
|
||||
const objWithItems: ObjWithItems = { items: {}}
|
||||
>objWithItems : ObjWithItems
|
||||
>{ items: {}} : { items: {}; }
|
||||
>items : {}
|
||||
>{} : {}
|
||||
|
||||
const itemOk2 = objWithItems.items[itemId]
|
||||
>itemOk2 : AB
|
||||
>objWithItems.items[itemId] : AB
|
||||
>objWithItems.items : { [s: string]: AB; }
|
||||
>objWithItems : ObjWithItems
|
||||
>items : { [s: string]: AB; }
|
||||
>itemId : "some-id"
|
||||
|
||||
typeof itemOk2 // pass
|
||||
>typeof itemOk2 : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>itemOk2 : AB
|
||||
|
||||
const {
|
||||
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
|
||||
>items : any
|
||||
>itemId : "some-id"
|
||||
>itemWithTSError : AB
|
||||
>{} : {}
|
||||
|
||||
} = objWithItems
|
||||
>objWithItems : ObjWithItems
|
||||
|
||||
// in order to re-produce the error, uncomment next line:
|
||||
typeof itemWithTSError // :(
|
||||
>typeof itemWithTSError : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>itemWithTSError : AB
|
||||
|
||||
// will result in:
|
||||
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
|
30
tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts
Normal file
30
tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
// https://github.com/Microsoft/TypeScript/issues/29006
|
||||
export interface A { type: 'a' }
|
||||
export interface B { type: 'b' }
|
||||
export type AB = A | B
|
||||
|
||||
const itemId = 'some-id'
|
||||
|
||||
// --- test on first level ---
|
||||
const items: { [id: string]: AB } = {}
|
||||
const { [itemId]: itemOk1 } = items
|
||||
typeof itemOk1 // pass
|
||||
|
||||
// --- test on second level ---
|
||||
interface ObjWithItems {
|
||||
items: {[s: string]: AB}
|
||||
}
|
||||
const objWithItems: ObjWithItems = { items: {}}
|
||||
|
||||
const itemOk2 = objWithItems.items[itemId]
|
||||
typeof itemOk2 // pass
|
||||
|
||||
const {
|
||||
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
|
||||
} = objWithItems
|
||||
|
||||
// in order to re-produce the error, uncomment next line:
|
||||
typeof itemWithTSError // :(
|
||||
|
||||
// will result in:
|
||||
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined
|
Loading…
Reference in a new issue