Skip to content

Commit 4c19873

Browse files
authored
Validate symbol-named properties against symbol index signatures (microsoft#44815)
* Validate symbols against both symbol and string index signatures * Add tests * Accept new baselines
1 parent 114f68c commit 4c19873

File tree

6 files changed

+155
-2
lines changed

6 files changed

+155
-2
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12106,7 +12106,7 @@ namespace ts {
1210612106
}
1210712107

1210812108
function getApplicableIndexInfoForName(type: Type, name: __String): IndexInfo | undefined {
12109-
return getApplicableIndexInfo(type, getStringLiteralType(unescapeLeadingUnderscores(name)));
12109+
return getApplicableIndexInfo(type, isLateBoundName(name) ? esSymbolType : getStringLiteralType(unescapeLeadingUnderscores(name)));
1211012110
}
1211112111

1211212112
// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
@@ -27121,8 +27121,12 @@ namespace ts {
2712127121
*/
2712227122
function isKnownProperty(targetType: Type, name: __String, isComparingJsxAttributes: boolean): boolean {
2712327123
if (targetType.flags & TypeFlags.Object) {
27124+
// For backwards compatibility a symbol-named property is satisfied by a string index signature. This
27125+
// is incorrect and inconsistent with element access expressions, where it is an error, so eventually
27126+
// we should remove this exception.
2712427127
if (getPropertyOfObjectType(targetType, name) ||
2712527128
getApplicableIndexInfoForName(targetType, name) ||
27129+
isLateBoundName(name) && getIndexInfoOfType(targetType, stringType) ||
2712627130
isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
2712727131
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
2712827132
return true;

tests/baselines/reference/indexSignatures1.errors.txt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ tests/cases/conformance/types/members/indexSignatures1.ts(281,35): error TS2322:
6868
Object literal may only specify known properties, and ''someKey'' does not exist in type 'PseudoDeclaration'.
6969
tests/cases/conformance/types/members/indexSignatures1.ts(286,7): error TS2322: Type '"two"' is not assignable to type '`/${string}`'.
7070
tests/cases/conformance/types/members/indexSignatures1.ts(289,7): error TS2322: Type 'number' is not assignable to type 'PathsObject'.
71+
tests/cases/conformance/types/members/indexSignatures1.ts(312,43): error TS2322: Type '{ [sym]: string; }' is not assignable to type '{ [key: number]: string; }'.
72+
Object literal may only specify known properties, and '[sym]' does not exist in type '{ [key: number]: string; }'.
7173

7274

73-
==== tests/cases/conformance/types/members/indexSignatures1.ts (49 errors) ====
75+
==== tests/cases/conformance/types/members/indexSignatures1.ts (50 errors) ====
7476
// Symbol index signature checking
7577

7678
const sym = Symbol();
@@ -488,4 +490,21 @@ tests/cases/conformance/types/members/indexSignatures1.ts(289,7): error TS2322:
488490
const a: A = { [id]: 'test' }
489491

490492
let aid = a[id];
493+
494+
// Repro from #44793
495+
496+
interface AA {
497+
a?: string;
498+
b?: number;
499+
[key: symbol]: string;
500+
}
501+
502+
const aa: AA = { [sym]: '123' };
503+
504+
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
505+
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
506+
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
507+
~~~~~~~~~~~~~~~
508+
!!! error TS2322: Type '{ [sym]: string; }' is not assignable to type '{ [key: number]: string; }'.
509+
!!! error TS2322: Object literal may only specify known properties, and '[sym]' does not exist in type '{ [key: number]: string; }'.
491510

tests/baselines/reference/indexSignatures1.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,20 @@ type A = Record<IdType, string>;
297297
const a: A = { [id]: 'test' }
298298

299299
let aid = a[id];
300+
301+
// Repro from #44793
302+
303+
interface AA {
304+
a?: string;
305+
b?: number;
306+
[key: symbol]: string;
307+
}
308+
309+
const aa: AA = { [sym]: '123' };
310+
311+
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
312+
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
313+
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
300314

301315

302316
//// [indexSignatures1.js]
@@ -457,6 +471,10 @@ const pathObject = 123; // Error
457471
const id = '0000-0000-0000-0001';
458472
const a = { [id]: 'test' };
459473
let aid = a[id];
474+
const aa = { [sym]: '123' };
475+
const obj1 = { [sym]: 'hello ' };
476+
const obj2 = { [sym]: 'hello ' }; // Permitted for backwards compatibility
477+
const obj3 = { [sym]: 'hello ' }; // Error
460478

461479

462480
//// [indexSignatures1.d.ts]
@@ -627,3 +645,18 @@ declare const id: IdType;
627645
declare type A = Record<IdType, string>;
628646
declare const a: A;
629647
declare let aid: string;
648+
interface AA {
649+
a?: string;
650+
b?: number;
651+
[key: symbol]: string;
652+
}
653+
declare const aa: AA;
654+
declare const obj1: {
655+
[key: symbol]: string;
656+
};
657+
declare const obj2: {
658+
[key: string]: string;
659+
};
660+
declare const obj3: {
661+
[key: number]: string;
662+
};

tests/baselines/reference/indexSignatures1.symbols

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,3 +860,42 @@ let aid = a[id];
860860
>a : Symbol(a, Decl(indexSignatures1.ts, 295, 5))
861861
>id : Symbol(id, Decl(indexSignatures1.ts, 291, 5))
862862

863+
// Repro from #44793
864+
865+
interface AA {
866+
>AA : Symbol(AA, Decl(indexSignatures1.ts, 297, 16))
867+
868+
a?: string;
869+
>a : Symbol(AA.a, Decl(indexSignatures1.ts, 301, 14))
870+
871+
b?: number;
872+
>b : Symbol(AA.b, Decl(indexSignatures1.ts, 302, 15))
873+
874+
[key: symbol]: string;
875+
>key : Symbol(key, Decl(indexSignatures1.ts, 304, 5))
876+
}
877+
878+
const aa: AA = { [sym]: '123' };
879+
>aa : Symbol(aa, Decl(indexSignatures1.ts, 307, 5))
880+
>AA : Symbol(AA, Decl(indexSignatures1.ts, 297, 16))
881+
>[sym] : Symbol([sym], Decl(indexSignatures1.ts, 307, 16))
882+
>sym : Symbol(sym, Decl(indexSignatures1.ts, 2, 5))
883+
884+
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
885+
>obj1 : Symbol(obj1, Decl(indexSignatures1.ts, 309, 5))
886+
>key : Symbol(key, Decl(indexSignatures1.ts, 309, 15))
887+
>[sym] : Symbol([sym], Decl(indexSignatures1.ts, 309, 41))
888+
>sym : Symbol(sym, Decl(indexSignatures1.ts, 2, 5))
889+
890+
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
891+
>obj2 : Symbol(obj2, Decl(indexSignatures1.ts, 310, 5))
892+
>key : Symbol(key, Decl(indexSignatures1.ts, 310, 15))
893+
>[sym] : Symbol([sym], Decl(indexSignatures1.ts, 310, 41))
894+
>sym : Symbol(sym, Decl(indexSignatures1.ts, 2, 5))
895+
896+
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
897+
>obj3 : Symbol(obj3, Decl(indexSignatures1.ts, 311, 5))
898+
>key : Symbol(key, Decl(indexSignatures1.ts, 311, 15))
899+
>[sym] : Symbol([sym], Decl(indexSignatures1.ts, 311, 41))
900+
>sym : Symbol(sym, Decl(indexSignatures1.ts, 2, 5))
901+

tests/baselines/reference/indexSignatures1.types

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,3 +1007,47 @@ let aid = a[id];
10071007
>a : A
10081008
>id : `${number}-${number}-${number}-${number}`
10091009

1010+
// Repro from #44793
1011+
1012+
interface AA {
1013+
a?: string;
1014+
>a : string | undefined
1015+
1016+
b?: number;
1017+
>b : number | undefined
1018+
1019+
[key: symbol]: string;
1020+
>key : symbol
1021+
}
1022+
1023+
const aa: AA = { [sym]: '123' };
1024+
>aa : AA
1025+
>{ [sym]: '123' } : { [sym]: string; }
1026+
>[sym] : string
1027+
>sym : unique symbol
1028+
>'123' : "123"
1029+
1030+
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
1031+
>obj1 : { [key: symbol]: string; }
1032+
>key : symbol
1033+
>{ [sym]: 'hello '} : { [sym]: string; }
1034+
>[sym] : string
1035+
>sym : unique symbol
1036+
>'hello ' : "hello "
1037+
1038+
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
1039+
>obj2 : { [key: string]: string; }
1040+
>key : string
1041+
>{ [sym]: 'hello '} : { [sym]: string; }
1042+
>[sym] : string
1043+
>sym : unique symbol
1044+
>'hello ' : "hello "
1045+
1046+
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
1047+
>obj3 : { [key: number]: string; }
1048+
>key : number
1049+
>{ [sym]: 'hello '} : { [sym]: string; }
1050+
>[sym] : string
1051+
>sym : unique symbol
1052+
>'hello ' : "hello "
1053+

tests/cases/conformance/types/members/indexSignatures1.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,17 @@ type A = Record<IdType, string>;
300300
const a: A = { [id]: 'test' }
301301

302302
let aid = a[id];
303+
304+
// Repro from #44793
305+
306+
interface AA {
307+
a?: string;
308+
b?: number;
309+
[key: symbol]: string;
310+
}
311+
312+
const aa: AA = { [sym]: '123' };
313+
314+
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
315+
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
316+
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error

0 commit comments

Comments
 (0)