Fix crash in getTextOfPropertyName

This commit is contained in:
Ron Buckton 2019-01-14 17:47:52 -08:00
parent d53619a30d
commit 208148d05c
6 changed files with 243 additions and 16 deletions

View file

@ -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);
}
}
}

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View 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