Replace most instances of getTextOfPropertyName in checker

This commit is contained in:
Ron Buckton 2019-01-22 14:24:21 -08:00
parent 208148d05c
commit 60639ce5a8
14 changed files with 188 additions and 56 deletions

View File

@ -5280,17 +5280,18 @@ namespace ts {
let objectFlags = ObjectFlags.ObjectLiteral;
forEach(pattern.elements, e => {
const name = e.propertyName || <Identifier>e.name;
if (isComputedNonLiteralName(name)) {
// do not include computed properties in the implied type
objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties;
return;
}
if (e.dotDotDotToken) {
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
return;
}
const text = getTextOfPropertyName(name);
const exprType = getLiteralTypeFromPropertyName(name);
if (!isTypeUsableAsPropertyName(exprType)) {
// do not include computed properties in the implied type
objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties;
return;
}
const text = getPropertyNameFromType(exprType);
const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = createSymbol(flags, text);
symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
@ -6399,9 +6400,9 @@ namespace ts {
}
/**
* Indicates whether a type can be used as a late-bound name.
* Indicates whether a type can be used as a property name.
*/
function isTypeUsableAsLateBoundName(type: Type): type is LiteralType | UniqueESSymbolType {
function isTypeUsableAsPropertyName(type: Type): type is StringLiteralType | NumberLiteralType | UniqueESSymbolType {
return !!(type.flags & TypeFlags.StringOrNumberLiteralOrUnique);
}
@ -6416,7 +6417,7 @@ namespace ts {
function isLateBindableName(node: DeclarationName): node is LateBoundName {
return isComputedPropertyName(node)
&& isEntityNameExpression(node.expression)
&& isTypeUsableAsLateBoundName(checkComputedPropertyName(node));
&& isTypeUsableAsPropertyName(checkComputedPropertyName(node));
}
function isLateBoundName(name: __String): boolean {
@ -6448,21 +6449,14 @@ namespace ts {
}
/**
* Gets the late-bound name for a computed property name.
* Gets the symbolic name for a member from its type.
*/
function getLateBoundName(node: LateBoundName) {
return getLateBoundNameFromType(checkComputedPropertyName(node));
}
/**
* Gets the symbolic name for a late-bound member from its type.
*/
function getLateBoundNameFromType(type: LiteralType | UniqueESSymbolType): __String {
function getPropertyNameFromType(type: StringLiteralType | NumberLiteralType | UniqueESSymbolType): __String {
if (type.flags & TypeFlags.UniqueESSymbol) {
return `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String;
return (<UniqueESSymbolType>type).escapedName;
}
if (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
return escapeLeadingUnderscores("" + (<LiteralType>type).value);
return escapeLeadingUnderscores("" + (<StringLiteralType | NumberLiteralType>type).value);
}
return Debug.fail();
}
@ -6525,8 +6519,8 @@ namespace ts {
// fall back to the early-bound name of this member.
links.resolvedSymbol = decl.symbol;
const type = checkComputedPropertyName(decl.name);
if (isTypeUsableAsLateBoundName(type)) {
const memberName = getLateBoundNameFromType(type);
if (isTypeUsableAsPropertyName(type)) {
const memberName = getPropertyNameFromType(type);
const symbolFlags = decl.symbol.flags;
// Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations.
@ -7175,8 +7169,8 @@ namespace ts {
const propType = instantiateType(templateType, templateMapper);
// If the current iteration type constituent is a string literal type, create a property.
// Otherwise, for type string create a string index signature.
if (t.flags & TypeFlags.StringOrNumberLiteralOrUnique) {
const propName = getLateBoundNameFromType(t as LiteralType);
if (isTypeUsableAsPropertyName(t)) {
const propName = getPropertyNameFromType(t);
const modifiersProp = getPropertyOfType(modifiersType, propName);
const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional ||
!(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
@ -7361,7 +7355,8 @@ 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 && !isComputedNonLiteralName(property.name) ? getTextOfPropertyName(property.name) : undefined;
const nameType = property.name && getLiteralTypeFromPropertyName(property.name);
const name = nameType && isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined;
const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name);
return !!expected && isLiteralType(expected) && !isTypeIdenticalTo(getTypeOfNode(property), expected);
});
@ -9724,8 +9719,8 @@ namespace ts {
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, cacheSymbol: boolean, missingType: Type) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
const propName = isTypeUsableAsLateBoundName(indexType) ?
getLateBoundNameFromType(indexType) :
const propName = isTypeUsableAsPropertyName(indexType) ?
getPropertyNameFromType(indexType) :
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>accessExpression.argumentExpression).name)) :
accessNode && isPropertyName(accessNode) ?
@ -10415,6 +10410,7 @@ namespace ts {
function createUniqueESSymbolType(symbol: Symbol) {
const type = <UniqueESSymbolType>createType(TypeFlags.UniqueESSymbol);
type.symbol = symbol;
type.escapedName = `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String;
return type;
}
@ -11302,7 +11298,7 @@ namespace ts {
}
if (resultObj.error) {
const reportedDiag = resultObj.error;
const propertyName = isTypeUsableAsLateBoundName(nameType) ? getLateBoundNameFromType(nameType) : undefined;
const propertyName = isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined;
const targetProp = propertyName !== undefined ? getPropertyOfType(target, propertyName) : undefined;
let issuedElaboration = false;
@ -15066,10 +15062,9 @@ namespace ts {
}
function getTypeOfDestructuredProperty(type: Type, name: PropertyName) {
const text = !isComputedNonLiteralName(name) ? getTextOfPropertyName(name) :
isLateBindableName(name) ? getLateBoundName(name) :
undefined;
if (text === undefined) return errorType;
const nameType = getLiteralTypeFromPropertyName(name);
if (!isTypeUsableAsPropertyName(nameType)) return errorType;
const text = getPropertyNameFromType(nameType);
return getConstraintForLocation(getTypeOfPropertyOfType(type, text), name) ||
isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) ||
getIndexTypeOfType(type, IndexKind.String) ||
@ -17202,8 +17197,11 @@ namespace ts {
const name = declaration.propertyName || declaration.name;
const parentType = getContextualTypeForVariableLikeDeclaration(parentDeclaration);
if (parentType && !isBindingPattern(name) && !isComputedNonLiteralName(name)) {
const text = getTextOfPropertyName(name);
return getTypeOfPropertyOfType(parentType, text);
const nameType = getLiteralTypeFromPropertyName(name);
if (isTypeUsableAsPropertyName(nameType)) {
const text = getPropertyNameFromType(nameType);
return getTypeOfPropertyOfType(parentType, text);
}
}
}
@ -18146,10 +18144,9 @@ namespace ts {
}
}
typeFlags |= type.flags;
const nameType = computedNameType && computedNameType.flags & TypeFlags.StringOrNumberLiteralOrUnique ?
<LiteralType | UniqueESSymbolType>computedNameType : undefined;
const nameType = computedNameType && isTypeUsableAsPropertyName(computedNameType) ? computedNameType : undefined;
const prop = nameType ?
createSymbol(SymbolFlags.Property | member.flags, getLateBoundNameFromType(nameType), CheckFlags.Late) :
createSymbol(SymbolFlags.Property | member.flags, getPropertyNameFromType(nameType), CheckFlags.Late) :
createSymbol(SymbolFlags.Property | member.flags, member.escapedName);
if (nameType) {
prop.nameType = nameType;
@ -22209,15 +22206,15 @@ 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;
if (!isComputedNonLiteralName(name)) {
const text = getTextOfPropertyName(name);
const exprType = getLiteralTypeFromPropertyName(name);
if (isTypeUsableAsPropertyName(exprType)) {
const text = getPropertyNameFromType(exprType);
const prop = getPropertyOfType(objectLiteralType, text);
if (prop) {
markPropertyAsReferenced(prop, property, rightIsThis);
checkPropertyAccessibility(property, /*isSuper*/ false, objectLiteralType, prop);
}
}
const exprType = getLiteralTypeFromPropertyName(name);
const elementType = getIndexedAccessType(objectLiteralType, exprType, name);
const type = getFlowTypeOfDestructuring(property, elementType);
return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type);
@ -25532,12 +25529,15 @@ namespace ts {
const parent = node.parent.parent;
const parentType = getTypeForBindingElementParent(parent);
const name = node.propertyName || node.name;
if (!isBindingPattern(name) && !isComputedNonLiteralName(name)) {
const nameText = getTextOfPropertyName(name);
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);
if (!isBindingPattern(name) && parentType) {
const exprType = getLiteralTypeFromPropertyName(name);
if (isTypeUsableAsPropertyName(exprType)) {
const nameText = getPropertyNameFromType(exprType);
const property = getPropertyOfType(parentType, nameText);
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

@ -3952,6 +3952,7 @@ namespace ts {
// Unique symbol types (TypeFlags.UniqueESSymbol)
export interface UniqueESSymbolType extends Type {
symbol: Symbol;
escapedName: __String;
}
export interface StringLiteralType extends LiteralType {

View File

@ -2214,6 +2214,7 @@ declare namespace ts {
}
interface UniqueESSymbolType extends Type {
symbol: Symbol;
escapedName: __String;
}
interface StringLiteralType extends LiteralType {
value: string;

View File

@ -2214,6 +2214,7 @@ declare namespace ts {
}
interface UniqueESSymbolType extends Type {
symbol: Symbol;
escapedName: __String;
}
interface StringLiteralType extends LiteralType {
value: string;

View File

@ -0,0 +1,36 @@
tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts(23,24): error TS2525: Initializer provides no value for this binding element and the binding element has no default value.
==== tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts (1 errors) ====
// 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*/
~~~~~~~~~~~~~~~
!!! error TS2525: Initializer provides no value for this binding element and the binding element has no default value.
} = 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

View File

@ -56,8 +56,8 @@ const {
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
>items : any
>itemId : "some-id"
>itemWithTSError : AB
>{} : {}
>itemWithTSError : any
>{} : { some-id: any; }
} = objWithItems
>objWithItems : ObjWithItems
@ -65,7 +65,7 @@ const {
// in order to re-produce the error, uncomment next line:
typeof itemWithTSError // :(
>typeof itemWithTSError : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>itemWithTSError : AB
>itemWithTSError : any
// will result in:
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined

View File

@ -1,9 +1,10 @@
tests/cases/compiler/destructureComputedProperty.ts(7,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
tests/cases/compiler/destructureComputedProperty.ts(8,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
tests/cases/compiler/destructureComputedProperty.ts(9,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
tests/cases/compiler/destructureComputedProperty.ts(10,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
==== tests/cases/compiler/destructureComputedProperty.ts (3 errors) ====
==== tests/cases/compiler/destructureComputedProperty.ts (4 errors) ====
declare const ab: { n: number } | { n: string };
const nameN = "n";
const { [nameN]: n } = ab;
@ -17,6 +18,8 @@ tests/cases/compiler/destructureComputedProperty.ts(10,7): error TS2341: Propert
~~~~~~~~~~~~~
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
const { [nameP]: p2 } = new C();
~~~~~~~~~~~~~~~
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
const { p: p3 } = new C();
~~~~~~~~~
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.

View File

@ -1,7 +1,8 @@
tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts(11,8): error TS2339: Property 'prop2' does not exist on type '{ prop: string; }'.
tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts(11,21): error TS2525: Initializer provides no value for this binding element and the binding element has no default value.
tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts(11,37): error TS2353: Object literal may only specify known properties, and 'prop' does not exist in type '{ prop2: any; }'.
==== tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts (1 errors) ====
==== tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts (2 errors) ====
let { [Symbol.iterator]: destructured } = [];
void destructured;
@ -13,6 +14,8 @@ tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts(11,8): error TS
const notPresent = "prop2";
let { [notPresent]: computed2 } = { prop: "b" };
~~~~~~~~~~
!!! error TS2339: Property 'prop2' does not exist on type '{ prop: string; }'.
~~~~~~~~~
!!! error TS2525: Initializer provides no value for this binding element and the binding element has no default value.
~~~~
!!! error TS2353: Object literal may only specify known properties, and 'prop' does not exist in type '{ prop2: any; }'.

View File

@ -32,7 +32,7 @@ const notPresent = "prop2";
let { [notPresent]: computed2 } = { prop: "b" };
>notPresent : "prop2"
>computed2 : any
>{ prop: "b" } : { prop: string; }
>{ prop: "b" } : { prop: string; prop2: any; }
>prop : string
>"b" : "b"

View File

@ -1,8 +1,10 @@
tests/cases/compiler/destructuringAssignment_private.ts(6,10): error TS2341: Property 'x' is private and only accessible within class 'C'.
tests/cases/compiler/destructuringAssignment_private.ts(7,4): error TS2341: Property 'o' is private and only accessible within class 'C'.
tests/cases/compiler/destructuringAssignment_private.ts(10,10): error TS2341: Property 'x' is private and only accessible within class 'C'.
tests/cases/compiler/destructuringAssignment_private.ts(13,4): error TS2341: Property 'o' is private and only accessible within class 'C'.
==== tests/cases/compiler/destructuringAssignment_private.ts (2 errors) ====
==== tests/cases/compiler/destructuringAssignment_private.ts (4 errors) ====
class C {
private x = 0;
private o = [{ a: 1 }];
@ -14,4 +16,14 @@ tests/cases/compiler/destructuringAssignment_private.ts(7,4): error TS2341: Prop
({ o: [{ a: x }]} = new C());
~
!!! error TS2341: Property 'o' is private and only accessible within class 'C'.
const nameX = "x";
([{ a: { [nameX]: x } }] = [{ a: new C() }]);
~~~~~~~
!!! error TS2341: Property 'x' is private and only accessible within class 'C'.
const nameO = "o";
({ [nameO]: [{ a: x }]} = new C());
~~~~~~~
!!! error TS2341: Property 'o' is private and only accessible within class 'C'.

View File

@ -6,9 +6,16 @@ class C {
let x: number;
([{ a: { x } }] = [{ a: new C() }]);
({ o: [{ a: x }]} = new C());
const nameX = "x";
([{ a: { [nameX]: x } }] = [{ a: new C() }]);
const nameO = "o";
({ [nameO]: [{ a: x }]} = new C());
//// [destructuringAssignment_private.js]
var _a, _b;
var C = /** @class */ (function () {
function C() {
this.x = 0;
@ -19,3 +26,7 @@ var C = /** @class */ (function () {
var x;
(x = [{ a: new C() }][0].a.x);
(x = new C().o[0].a);
var nameX = "x";
(_a = nameX, x = [{ a: new C() }][0].a[_a]);
var nameO = "o";
(_b = nameO, x = new C()[_b][0].a);

View File

@ -24,3 +24,24 @@ let x: number;
>x : Symbol(x, Decl(destructuringAssignment_private.ts, 4, 3))
>C : Symbol(C, Decl(destructuringAssignment_private.ts, 0, 0))
const nameX = "x";
>nameX : Symbol(nameX, Decl(destructuringAssignment_private.ts, 8, 5))
([{ a: { [nameX]: x } }] = [{ a: new C() }]);
>a : Symbol(a, Decl(destructuringAssignment_private.ts, 9, 3))
>[nameX] : Symbol([nameX], Decl(destructuringAssignment_private.ts, 9, 8))
>nameX : Symbol(nameX, Decl(destructuringAssignment_private.ts, 8, 5))
>x : Symbol(x, Decl(destructuringAssignment_private.ts, 4, 3))
>a : Symbol(a, Decl(destructuringAssignment_private.ts, 9, 29))
>C : Symbol(C, Decl(destructuringAssignment_private.ts, 0, 0))
const nameO = "o";
>nameO : Symbol(nameO, Decl(destructuringAssignment_private.ts, 11, 5))
({ [nameO]: [{ a: x }]} = new C());
>[nameO] : Symbol([nameO], Decl(destructuringAssignment_private.ts, 12, 2))
>nameO : Symbol(nameO, Decl(destructuringAssignment_private.ts, 11, 5))
>a : Symbol(a, Decl(destructuringAssignment_private.ts, 12, 14))
>x : Symbol(x, Decl(destructuringAssignment_private.ts, 4, 3))
>C : Symbol(C, Decl(destructuringAssignment_private.ts, 0, 0))

View File

@ -42,3 +42,40 @@ let x: number;
>new C() : C
>C : typeof C
const nameX = "x";
>nameX : "x"
>"x" : "x"
([{ a: { [nameX]: x } }] = [{ a: new C() }]);
>([{ a: { [nameX]: x } }] = [{ a: new C() }]) : [{ a: C; }]
>[{ a: { [nameX]: x } }] = [{ a: new C() }] : [{ a: C; }]
>[{ a: { [nameX]: x } }] : [{ a: { [nameX]: number; }; }]
>{ a: { [nameX]: x } } : { a: { [nameX]: number; }; }
>a : { [nameX]: number; }
>{ [nameX]: x } : { [nameX]: number; }
>[nameX] : number
>nameX : "x"
>x : number
>[{ a: new C() }] : [{ a: C; }]
>{ a: new C() } : { a: C; }
>a : C
>new C() : C
>C : typeof C
const nameO = "o";
>nameO : "o"
>"o" : "o"
({ [nameO]: [{ a: x }]} = new C());
>({ [nameO]: [{ a: x }]} = new C()) : C
>{ [nameO]: [{ a: x }]} = new C() : C
>{ [nameO]: [{ a: x }]} : { [nameO]: [{ a: number; }]; }
>[nameO] : [{ a: number; }]
>nameO : "o"
>[{ a: x }] : [{ a: number; }]
>{ a: x } : { a: number; }
>a : number
>x : number
>new C() : C
>C : typeof C

View File

@ -5,3 +5,9 @@ class C {
let x: number;
([{ a: { x } }] = [{ a: new C() }]);
({ o: [{ a: x }]} = new C());
const nameX = "x";
([{ a: { [nameX]: x } }] = [{ a: new C() }]);
const nameO = "o";
({ [nameO]: [{ a: x }]} = new C());