Skip to content

Commit 60639ce

Browse files
committed
Replace most instances of getTextOfPropertyName in checker
1 parent 208148d commit 60639ce

14 files changed

+188
-56
lines changed

src/compiler/checker.ts

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5280,17 +5280,18 @@ namespace ts {
52805280
let objectFlags = ObjectFlags.ObjectLiteral;
52815281
forEach(pattern.elements, e => {
52825282
const name = e.propertyName || <Identifier>e.name;
5283-
if (isComputedNonLiteralName(name)) {
5284-
// do not include computed properties in the implied type
5285-
objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties;
5286-
return;
5287-
}
52885283
if (e.dotDotDotToken) {
52895284
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
52905285
return;
52915286
}
52925287

5293-
const text = getTextOfPropertyName(name);
5288+
const exprType = getLiteralTypeFromPropertyName(name);
5289+
if (!isTypeUsableAsPropertyName(exprType)) {
5290+
// do not include computed properties in the implied type
5291+
objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties;
5292+
return;
5293+
}
5294+
const text = getPropertyNameFromType(exprType);
52945295
const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0);
52955296
const symbol = createSymbol(flags, text);
52965297
symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
@@ -6399,9 +6400,9 @@ namespace ts {
63996400
}
64006401

64016402
/**
6402-
* Indicates whether a type can be used as a late-bound name.
6403+
* Indicates whether a type can be used as a property name.
64036404
*/
6404-
function isTypeUsableAsLateBoundName(type: Type): type is LiteralType | UniqueESSymbolType {
6405+
function isTypeUsableAsPropertyName(type: Type): type is StringLiteralType | NumberLiteralType | UniqueESSymbolType {
64056406
return !!(type.flags & TypeFlags.StringOrNumberLiteralOrUnique);
64066407
}
64076408

@@ -6416,7 +6417,7 @@ namespace ts {
64166417
function isLateBindableName(node: DeclarationName): node is LateBoundName {
64176418
return isComputedPropertyName(node)
64186419
&& isEntityNameExpression(node.expression)
6419-
&& isTypeUsableAsLateBoundName(checkComputedPropertyName(node));
6420+
&& isTypeUsableAsPropertyName(checkComputedPropertyName(node));
64206421
}
64216422

64226423
function isLateBoundName(name: __String): boolean {
@@ -6448,21 +6449,14 @@ namespace ts {
64486449
}
64496450

64506451
/**
6451-
* Gets the late-bound name for a computed property name.
6452+
* Gets the symbolic name for a member from its type.
64526453
*/
6453-
function getLateBoundName(node: LateBoundName) {
6454-
return getLateBoundNameFromType(checkComputedPropertyName(node));
6455-
}
6456-
6457-
/**
6458-
* Gets the symbolic name for a late-bound member from its type.
6459-
*/
6460-
function getLateBoundNameFromType(type: LiteralType | UniqueESSymbolType): __String {
6454+
function getPropertyNameFromType(type: StringLiteralType | NumberLiteralType | UniqueESSymbolType): __String {
64616455
if (type.flags & TypeFlags.UniqueESSymbol) {
6462-
return `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String;
6456+
return (<UniqueESSymbolType>type).escapedName;
64636457
}
64646458
if (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
6465-
return escapeLeadingUnderscores("" + (<LiteralType>type).value);
6459+
return escapeLeadingUnderscores("" + (<StringLiteralType | NumberLiteralType>type).value);
64666460
}
64676461
return Debug.fail();
64686462
}
@@ -6525,8 +6519,8 @@ namespace ts {
65256519
// fall back to the early-bound name of this member.
65266520
links.resolvedSymbol = decl.symbol;
65276521
const type = checkComputedPropertyName(decl.name);
6528-
if (isTypeUsableAsLateBoundName(type)) {
6529-
const memberName = getLateBoundNameFromType(type);
6522+
if (isTypeUsableAsPropertyName(type)) {
6523+
const memberName = getPropertyNameFromType(type);
65306524
const symbolFlags = decl.symbol.flags;
65316525

65326526
// 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 {
71757169
const propType = instantiateType(templateType, templateMapper);
71767170
// If the current iteration type constituent is a string literal type, create a property.
71777171
// Otherwise, for type string create a string index signature.
7178-
if (t.flags & TypeFlags.StringOrNumberLiteralOrUnique) {
7179-
const propName = getLateBoundNameFromType(t as LiteralType);
7172+
if (isTypeUsableAsPropertyName(t)) {
7173+
const propName = getPropertyNameFromType(t);
71807174
const modifiersProp = getPropertyOfType(modifiersType, propName);
71817175
const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional ||
71827176
!(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
@@ -7361,7 +7355,8 @@ namespace ts {
73617355
function isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean {
73627356
const list = obj.properties as NodeArray<ObjectLiteralElementLike | JsxAttributeLike>;
73637357
return list.some(property => {
7364-
const name = property.name && !isComputedNonLiteralName(property.name) ? getTextOfPropertyName(property.name) : undefined;
7358+
const nameType = property.name && getLiteralTypeFromPropertyName(property.name);
7359+
const name = nameType && isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined;
73657360
const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name);
73667361
return !!expected && isLiteralType(expected) && !isTypeIdenticalTo(getTypeOfNode(property), expected);
73677362
});
@@ -9724,8 +9719,8 @@ namespace ts {
97249719

97259720
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, cacheSymbol: boolean, missingType: Type) {
97269721
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
9727-
const propName = isTypeUsableAsLateBoundName(indexType) ?
9728-
getLateBoundNameFromType(indexType) :
9722+
const propName = isTypeUsableAsPropertyName(indexType) ?
9723+
getPropertyNameFromType(indexType) :
97299724
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
97309725
getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>accessExpression.argumentExpression).name)) :
97319726
accessNode && isPropertyName(accessNode) ?
@@ -10415,6 +10410,7 @@ namespace ts {
1041510410
function createUniqueESSymbolType(symbol: Symbol) {
1041610411
const type = <UniqueESSymbolType>createType(TypeFlags.UniqueESSymbol);
1041710412
type.symbol = symbol;
10413+
type.escapedName = `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String;
1041810414
return type;
1041910415
}
1042010416

@@ -11302,7 +11298,7 @@ namespace ts {
1130211298
}
1130311299
if (resultObj.error) {
1130411300
const reportedDiag = resultObj.error;
11305-
const propertyName = isTypeUsableAsLateBoundName(nameType) ? getLateBoundNameFromType(nameType) : undefined;
11301+
const propertyName = isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined;
1130611302
const targetProp = propertyName !== undefined ? getPropertyOfType(target, propertyName) : undefined;
1130711303

1130811304
let issuedElaboration = false;
@@ -15066,10 +15062,9 @@ namespace ts {
1506615062
}
1506715063

1506815064
function getTypeOfDestructuredProperty(type: Type, name: PropertyName) {
15069-
const text = !isComputedNonLiteralName(name) ? getTextOfPropertyName(name) :
15070-
isLateBindableName(name) ? getLateBoundName(name) :
15071-
undefined;
15072-
if (text === undefined) return errorType;
15065+
const nameType = getLiteralTypeFromPropertyName(name);
15066+
if (!isTypeUsableAsPropertyName(nameType)) return errorType;
15067+
const text = getPropertyNameFromType(nameType);
1507315068
return getConstraintForLocation(getTypeOfPropertyOfType(type, text), name) ||
1507415069
isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) ||
1507515070
getIndexTypeOfType(type, IndexKind.String) ||
@@ -17202,8 +17197,11 @@ namespace ts {
1720217197
const name = declaration.propertyName || declaration.name;
1720317198
const parentType = getContextualTypeForVariableLikeDeclaration(parentDeclaration);
1720417199
if (parentType && !isBindingPattern(name) && !isComputedNonLiteralName(name)) {
17205-
const text = getTextOfPropertyName(name);
17206-
return getTypeOfPropertyOfType(parentType, text);
17200+
const nameType = getLiteralTypeFromPropertyName(name);
17201+
if (isTypeUsableAsPropertyName(nameType)) {
17202+
const text = getPropertyNameFromType(nameType);
17203+
return getTypeOfPropertyOfType(parentType, text);
17204+
}
1720717205
}
1720817206
}
1720917207

@@ -18146,10 +18144,9 @@ namespace ts {
1814618144
}
1814718145
}
1814818146
typeFlags |= type.flags;
18149-
const nameType = computedNameType && computedNameType.flags & TypeFlags.StringOrNumberLiteralOrUnique ?
18150-
<LiteralType | UniqueESSymbolType>computedNameType : undefined;
18147+
const nameType = computedNameType && isTypeUsableAsPropertyName(computedNameType) ? computedNameType : undefined;
1815118148
const prop = nameType ?
18152-
createSymbol(SymbolFlags.Property | member.flags, getLateBoundNameFromType(nameType), CheckFlags.Late) :
18149+
createSymbol(SymbolFlags.Property | member.flags, getPropertyNameFromType(nameType), CheckFlags.Late) :
1815318150
createSymbol(SymbolFlags.Property | member.flags, member.escapedName);
1815418151
if (nameType) {
1815518152
prop.nameType = nameType;
@@ -22209,15 +22206,15 @@ namespace ts {
2220922206
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: NodeArray<ObjectLiteralElementLike>, rightIsThis = false) {
2221022207
if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) {
2221122208
const name = property.name;
22212-
if (!isComputedNonLiteralName(name)) {
22213-
const text = getTextOfPropertyName(name);
22209+
const exprType = getLiteralTypeFromPropertyName(name);
22210+
if (isTypeUsableAsPropertyName(exprType)) {
22211+
const text = getPropertyNameFromType(exprType);
2221422212
const prop = getPropertyOfType(objectLiteralType, text);
2221522213
if (prop) {
2221622214
markPropertyAsReferenced(prop, property, rightIsThis);
2221722215
checkPropertyAccessibility(property, /*isSuper*/ false, objectLiteralType, prop);
2221822216
}
2221922217
}
22220-
const exprType = getLiteralTypeFromPropertyName(name);
2222122218
const elementType = getIndexedAccessType(objectLiteralType, exprType, name);
2222222219
const type = getFlowTypeOfDestructuring(property, elementType);
2222322220
return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type);
@@ -25532,12 +25529,15 @@ namespace ts {
2553225529
const parent = node.parent.parent;
2553325530
const parentType = getTypeForBindingElementParent(parent);
2553425531
const name = node.propertyName || node.name;
25535-
if (!isBindingPattern(name) && !isComputedNonLiteralName(name)) {
25536-
const nameText = getTextOfPropertyName(name);
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);
25532+
if (!isBindingPattern(name) && parentType) {
25533+
const exprType = getLiteralTypeFromPropertyName(name);
25534+
if (isTypeUsableAsPropertyName(exprType)) {
25535+
const nameText = getPropertyNameFromType(exprType);
25536+
const property = getPropertyOfType(parentType, nameText);
25537+
if (property) {
25538+
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
25539+
checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
25540+
}
2554125541
}
2554225542
}
2554325543
}

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3952,6 +3952,7 @@ namespace ts {
39523952
// Unique symbol types (TypeFlags.UniqueESSymbol)
39533953
export interface UniqueESSymbolType extends Type {
39543954
symbol: Symbol;
3955+
escapedName: __String;
39553956
}
39563957

39573958
export interface StringLiteralType extends LiteralType {

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,7 @@ declare namespace ts {
22142214
}
22152215
interface UniqueESSymbolType extends Type {
22162216
symbol: Symbol;
2217+
escapedName: __String;
22172218
}
22182219
interface StringLiteralType extends LiteralType {
22192220
value: string;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,7 @@ declare namespace ts {
22142214
}
22152215
interface UniqueESSymbolType extends Type {
22162216
symbol: Symbol;
2217+
escapedName: __String;
22172218
}
22182219
interface StringLiteralType extends LiteralType {
22192220
value: string;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
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.
2+
3+
4+
==== tests/cases/compiler/crashInGetTextOfComputedPropertyName.ts (1 errors) ====
5+
// https://github.com/Microsoft/TypeScript/issues/29006
6+
export interface A { type: 'a' }
7+
export interface B { type: 'b' }
8+
export type AB = A | B
9+
10+
const itemId = 'some-id'
11+
12+
// --- test on first level ---
13+
const items: { [id: string]: AB } = {}
14+
const { [itemId]: itemOk1 } = items
15+
typeof itemOk1 // pass
16+
17+
// --- test on second level ---
18+
interface ObjWithItems {
19+
items: {[s: string]: AB}
20+
}
21+
const objWithItems: ObjWithItems = { items: {}}
22+
23+
const itemOk2 = objWithItems.items[itemId]
24+
typeof itemOk2 // pass
25+
26+
const {
27+
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
28+
~~~~~~~~~~~~~~~
29+
!!! error TS2525: Initializer provides no value for this binding element and the binding element has no default value.
30+
} = objWithItems
31+
32+
// in order to re-produce the error, uncomment next line:
33+
typeof itemWithTSError // :(
34+
35+
// will result in:
36+
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined

tests/baselines/reference/crashInGetTextOfComputedPropertyName.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@ const {
5656
items: { [itemId]: itemWithTSError } = {} /*happens when default value is provided*/
5757
>items : any
5858
>itemId : "some-id"
59-
>itemWithTSError : AB
60-
>{} : {}
59+
>itemWithTSError : any
60+
>{} : { some-id: any; }
6161

6262
} = objWithItems
6363
>objWithItems : ObjWithItems
6464

6565
// in order to re-produce the error, uncomment next line:
6666
typeof itemWithTSError // :(
6767
>typeof itemWithTSError : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
68-
>itemWithTSError : AB
68+
>itemWithTSError : any
6969

7070
// will result in:
7171
// Error from compilation: TypeError: Cannot read property 'charCodeAt' of undefined TypeError: Cannot read property 'charCodeAt' of undefined

tests/baselines/reference/destructureComputedProperty.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
tests/cases/compiler/destructureComputedProperty.ts(7,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
22
tests/cases/compiler/destructureComputedProperty.ts(8,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
3+
tests/cases/compiler/destructureComputedProperty.ts(9,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
34
tests/cases/compiler/destructureComputedProperty.ts(10,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
45

56

6-
==== tests/cases/compiler/destructureComputedProperty.ts (3 errors) ====
7+
==== tests/cases/compiler/destructureComputedProperty.ts (4 errors) ====
78
declare const ab: { n: number } | { n: string };
89
const nameN = "n";
910
const { [nameN]: n } = ab;
@@ -17,6 +18,8 @@ tests/cases/compiler/destructureComputedProperty.ts(10,7): error TS2341: Propert
1718
~~~~~~~~~~~~~
1819
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
1920
const { [nameP]: p2 } = new C();
21+
~~~~~~~~~~~~~~~
22+
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
2023
const { p: p3 } = new C();
2124
~~~~~~~~~
2225
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts(11,8): error TS2339: Property 'prop2' does not exist on type '{ prop: string; }'.
1+
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.
2+
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; }'.
23

34

4-
==== tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts (1 errors) ====
5+
==== tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts (2 errors) ====
56
let { [Symbol.iterator]: destructured } = [];
67
void destructured;
78

@@ -13,6 +14,8 @@ tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts(11,8): error TS
1314
const notPresent = "prop2";
1415

1516
let { [notPresent]: computed2 } = { prop: "b" };
16-
~~~~~~~~~~
17-
!!! error TS2339: Property 'prop2' does not exist on type '{ prop: string; }'.
17+
~~~~~~~~~
18+
!!! error TS2525: Initializer provides no value for this binding element and the binding element has no default value.
19+
~~~~
20+
!!! error TS2353: Object literal may only specify known properties, and 'prop' does not exist in type '{ prop2: any; }'.
1821

tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const notPresent = "prop2";
3232
let { [notPresent]: computed2 } = { prop: "b" };
3333
>notPresent : "prop2"
3434
>computed2 : any
35-
>{ prop: "b" } : { prop: string; }
35+
>{ prop: "b" } : { prop: string; prop2: any; }
3636
>prop : string
3737
>"b" : "b"
3838

0 commit comments

Comments
 (0)