Skip to content

Commit d07ed67

Browse files
committed
Support indexing with known symbols
1 parent df826de commit d07ed67

26 files changed

+495
-14
lines changed

src/compiler/binder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ module ts {
9797
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
9898
var nameExpression = (<ComputedPropertyName>node.name).expression;
9999
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
100-
return "__@" + (<PropertyAccessExpression>nameExpression).name.text;
100+
return getPropertyNameForKnownSymbolName((<PropertyAccessExpression>nameExpression).name.text);
101101
}
102102
return (<Identifier | LiteralExpression>node.name).text;
103103
}

src/compiler/checker.ts

+33-3
Original file line numberDiff line numberDiff line change
@@ -5787,8 +5787,8 @@ module ts {
57875787

57885788
// See if we can index as a property.
57895789
if (node.argumentExpression) {
5790-
if (node.argumentExpression.kind === SyntaxKind.StringLiteral || node.argumentExpression.kind === SyntaxKind.NumericLiteral) {
5791-
var name = (<LiteralExpression>node.argumentExpression).text;
5790+
var name = getPropertyNameForIndexedAccess(node.argumentExpression);
5791+
if (name !== undefined) {
57925792
var prop = getPropertyOfType(objectType, name);
57935793
if (prop) {
57945794
getNodeLinks(node).resolvedSymbol = prop;
@@ -5832,6 +5832,36 @@ module ts {
58325832
return unknownType;
58335833
}
58345834

5835+
/**
5836+
* If indexArgumentExpression is a string literal or number literal, returns its text.
5837+
* If indexArgumentExpression is a well known symbol, returns the property name corresponding
5838+
* to this symbol.
5839+
* Otherwise, returns undefined.
5840+
*/
5841+
function getPropertyNameForIndexedAccess(indexArgumentExpression: Expression) {
5842+
if (indexArgumentExpression.kind === SyntaxKind.StringLiteral || indexArgumentExpression.kind === SyntaxKind.NumericLiteral) {
5843+
return (<LiteralExpression>indexArgumentExpression).text;
5844+
}
5845+
if (isWellKnownSymbolSyntactically(indexArgumentExpression)) {
5846+
var leftHandSide = (<PropertyAccessExpression>indexArgumentExpression).expression;
5847+
Debug.assert((<Identifier>leftHandSide).text === "Symbol");
5848+
// The name is Symbol.<someName>, so make sure Symbol actually resolves to the
5849+
// global Symbol object
5850+
var leftHandSideSymbol = resolveName(indexArgumentExpression, (<Identifier>leftHandSide).text,
5851+
SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
5852+
if (leftHandSideSymbol === globalESSymbolConstructorSymbol) {
5853+
// Make sure the property type is the primitive symbol type
5854+
var rightHandSideName = (<Identifier>(<PropertyAccessExpression>indexArgumentExpression).name).text;
5855+
var esSymbolConstructorPropertyType = getTypeOfPropertyOfType(globalESSymbolConstructorType, rightHandSideName);
5856+
if (esSymbolConstructorPropertyType && esSymbolConstructorPropertyType.flags & TypeFlags.ESSymbol) {
5857+
return getPropertyNameForKnownSymbolName(rightHandSideName);
5858+
}
5859+
}
5860+
}
5861+
5862+
return undefined;
5863+
}
5864+
58355865
function resolveUntypedCall(node: CallLikeExpression): Signature {
58365866
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
58375867
checkExpression((<TaggedTemplateExpression>node).template);
@@ -10334,7 +10364,7 @@ module ts {
1033410364
globalTemplateStringsArrayType = getGlobalType("TemplateStringsArray");
1033510365
globalESSymbolType = getGlobalType("Symbol");
1033610366
globalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol");
10337-
globalESSymbolConstructorType = getTypeOfGlobalSymbol(globalESSymbolConstructorSymbol, /*arity*/ 0);
10367+
globalESSymbolConstructorType = getTypeOfSymbol(globalESSymbolConstructorSymbol);
1033810368
}
1033910369
else {
1034010370
globalTemplateStringsArrayType = unknownType;

src/compiler/utilities.ts

+4
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,10 @@ module ts {
852852
return node.kind === SyntaxKind.PropertyAccessExpression && isESSymbolIdentifier((<PropertyAccessExpression>node).expression);
853853
}
854854

855+
export function getPropertyNameForKnownSymbolName(symbolName: string): string {
856+
return "__@" + symbolName;
857+
}
858+
855859
/**
856860
* Includes the word "Symbol" with unicode escapes
857861
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/conformance/es6/Symbols/symbolProperty17.ts ===
2+
interface I {
3+
>I : I
4+
5+
[Symbol.iterator]: number;
6+
>Symbol.iterator : symbol
7+
>Symbol : SymbolConstructor
8+
>iterator : symbol
9+
10+
[s: symbol]: string;
11+
>s : symbol
12+
13+
"__@iterator": string;
14+
}
15+
16+
var i: I;
17+
>i : I
18+
>I : I
19+
20+
var it = i[Symbol.iterator];
21+
>it : number
22+
>i[Symbol.iterator] : number
23+
>i : I
24+
>Symbol.iterator : symbol
25+
>Symbol : SymbolConstructor
26+
>iterator : symbol
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/conformance/es6/Symbols/symbolProperty18.ts ===
2+
var i = {
3+
>i : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
4+
>{ [Symbol.iterator]: 0, [Symbol.toStringTag]() { return "" }, set [Symbol.toPrimitive](p: boolean) { }} : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
5+
6+
[Symbol.iterator]: 0,
7+
>Symbol.iterator : symbol
8+
>Symbol : SymbolConstructor
9+
>iterator : symbol
10+
11+
[Symbol.toStringTag]() { return "" },
12+
>Symbol.toStringTag : symbol
13+
>Symbol : SymbolConstructor
14+
>toStringTag : symbol
15+
16+
set [Symbol.toPrimitive](p: boolean) { }
17+
>Symbol.toPrimitive : symbol
18+
>Symbol : SymbolConstructor
19+
>toPrimitive : symbol
20+
>p : boolean
21+
}
22+
23+
var it = i[Symbol.iterator];
24+
>it : number
25+
>i[Symbol.iterator] : number
26+
>i : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
27+
>Symbol.iterator : symbol
28+
>Symbol : SymbolConstructor
29+
>iterator : symbol
30+
31+
var str = i[Symbol.toStringTag]();
32+
>str : string
33+
>i[Symbol.toStringTag]() : string
34+
>i[Symbol.toStringTag] : () => string
35+
>i : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
36+
>Symbol.toStringTag : symbol
37+
>Symbol : SymbolConstructor
38+
>toStringTag : symbol
39+
40+
i[Symbol.toPrimitive] = false;
41+
>i[Symbol.toPrimitive] = false : boolean
42+
>i[Symbol.toPrimitive] : boolean
43+
>i : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
44+
>Symbol.toPrimitive : symbol
45+
>Symbol : SymbolConstructor
46+
>toPrimitive : symbol
47+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/conformance/es6/Symbols/symbolProperty19.ts ===
2+
var i = {
3+
>i : { [Symbol.iterator]: { p: any; }; [Symbol.toStringTag](): { p: any; }; }
4+
>{ [Symbol.iterator]: { p: null }, [Symbol.toStringTag]() { return { p: undefined }; }} : { [Symbol.iterator]: { p: null; }; [Symbol.toStringTag](): { p: any; }; }
5+
6+
[Symbol.iterator]: { p: null },
7+
>Symbol.iterator : symbol
8+
>Symbol : SymbolConstructor
9+
>iterator : symbol
10+
>{ p: null } : { p: null; }
11+
>p : null
12+
13+
[Symbol.toStringTag]() { return { p: undefined }; }
14+
>Symbol.toStringTag : symbol
15+
>Symbol : SymbolConstructor
16+
>toStringTag : symbol
17+
>{ p: undefined } : { p: undefined; }
18+
>p : undefined
19+
>undefined : undefined
20+
}
21+
22+
var it = i[Symbol.iterator];
23+
>it : { p: any; }
24+
>i[Symbol.iterator] : { p: any; }
25+
>i : { [Symbol.iterator]: { p: any; }; [Symbol.toStringTag](): { p: any; }; }
26+
>Symbol.iterator : symbol
27+
>Symbol : SymbolConstructor
28+
>iterator : symbol
29+
30+
var str = i[Symbol.toStringTag]();
31+
>str : { p: any; }
32+
>i[Symbol.toStringTag]() : { p: any; }
33+
>i[Symbol.toStringTag] : () => { p: any; }
34+
>i : { [Symbol.iterator]: { p: any; }; [Symbol.toStringTag](): { p: any; }; }
35+
>Symbol.toStringTag : symbol
36+
>Symbol : SymbolConstructor
37+
>toStringTag : symbol
38+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/conformance/es6/Symbols/symbolProperty28.ts ===
2+
class C1 {
3+
>C1 : C1
4+
5+
[Symbol.toStringTag]() {
6+
>Symbol.toStringTag : symbol
7+
>Symbol : SymbolConstructor
8+
>toStringTag : symbol
9+
10+
return { x: "" };
11+
>{ x: "" } : { x: string; }
12+
>x : string
13+
}
14+
}
15+
16+
class C2 extends C1 { }
17+
>C2 : C2
18+
>C1 : C1
19+
20+
var c: C2;
21+
>c : C2
22+
>C2 : C2
23+
24+
var obj = c[Symbol.toStringTag]().x;
25+
>obj : string
26+
>c[Symbol.toStringTag]().x : string
27+
>c[Symbol.toStringTag]() : { x: string; }
28+
>c[Symbol.toStringTag] : () => { x: string; }
29+
>c : C2
30+
>Symbol.toStringTag : symbol
31+
>Symbol : SymbolConstructor
32+
>toStringTag : symbol
33+
>x : string
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
=== tests/cases/conformance/es6/Symbols/symbolProperty40.ts ===
2+
class C {
3+
>C : C
4+
5+
[Symbol.iterator](x: string): string;
6+
>Symbol.iterator : symbol
7+
>Symbol : SymbolConstructor
8+
>iterator : symbol
9+
>x : string
10+
11+
[Symbol.iterator](x: number): number;
12+
>Symbol.iterator : symbol
13+
>Symbol : SymbolConstructor
14+
>iterator : symbol
15+
>x : number
16+
17+
[Symbol.iterator](x: any) {
18+
>Symbol.iterator : symbol
19+
>Symbol : SymbolConstructor
20+
>iterator : symbol
21+
>x : any
22+
23+
return undefined;
24+
>undefined : undefined
25+
}
26+
}
27+
28+
var c = new C;
29+
>c : C
30+
>new C : C
31+
>C : typeof C
32+
33+
c[Symbol.iterator]("");
34+
>c[Symbol.iterator]("") : string
35+
>c[Symbol.iterator] : { (x: string): string; (x: number): number; }
36+
>c : C
37+
>Symbol.iterator : symbol
38+
>Symbol : SymbolConstructor
39+
>iterator : symbol
40+
41+
c[Symbol.iterator](0);
42+
>c[Symbol.iterator](0) : number
43+
>c[Symbol.iterator] : { (x: string): string; (x: number): number; }
44+
>c : C
45+
>Symbol.iterator : symbol
46+
>Symbol : SymbolConstructor
47+
>iterator : symbol
48+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=== tests/cases/conformance/es6/Symbols/symbolProperty41.ts ===
2+
class C {
3+
>C : C
4+
5+
[Symbol.iterator](x: string): { x: string };
6+
>Symbol.iterator : symbol
7+
>Symbol : SymbolConstructor
8+
>iterator : symbol
9+
>x : string
10+
>x : string
11+
12+
[Symbol.iterator](x: "hello"): { x: string; hello: string };
13+
>Symbol.iterator : symbol
14+
>Symbol : SymbolConstructor
15+
>iterator : symbol
16+
>x : "hello"
17+
>x : string
18+
>hello : string
19+
20+
[Symbol.iterator](x: any) {
21+
>Symbol.iterator : symbol
22+
>Symbol : SymbolConstructor
23+
>iterator : symbol
24+
>x : any
25+
26+
return undefined;
27+
>undefined : undefined
28+
}
29+
}
30+
31+
var c = new C;
32+
>c : C
33+
>new C : C
34+
>C : typeof C
35+
36+
c[Symbol.iterator]("");
37+
>c[Symbol.iterator]("") : { x: string; }
38+
>c[Symbol.iterator] : { (x: string): { x: string; }; (x: "hello"): { x: string; hello: string; }; }
39+
>c : C
40+
>Symbol.iterator : symbol
41+
>Symbol : SymbolConstructor
42+
>iterator : symbol
43+
44+
c[Symbol.iterator]("hello");
45+
>c[Symbol.iterator]("hello") : { x: string; hello: string; }
46+
>c[Symbol.iterator] : { (x: string): { x: string; }; (x: "hello"): { x: string; hello: string; }; }
47+
>c : C
48+
>Symbol.iterator : symbol
49+
>Symbol : SymbolConstructor
50+
>iterator : symbol
51+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
tests/cases/conformance/es6/Symbols/symbolProperty46.ts(10,1): error TS2322: Type 'number' is not assignable to type 'string'.
2+
3+
4+
==== tests/cases/conformance/es6/Symbols/symbolProperty46.ts (1 errors) ====
5+
class C {
6+
get [Symbol.hasInstance]() {
7+
return "";
8+
}
9+
// Should take a string
10+
set [Symbol.hasInstance](x) {
11+
}
12+
}
13+
14+
(new C)[Symbol.hasInstance] = 0;
15+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
16+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
17+
(new C)[Symbol.hasInstance] = "";

tests/baselines/reference/symbolProperty47.errors.txt

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
tests/cases/conformance/es6/Symbols/symbolProperty47.ts(3,16): error TS2322: Type 'string' is not assignable to type 'number'.
2+
tests/cases/conformance/es6/Symbols/symbolProperty47.ts(11,1): error TS2322: Type 'string' is not assignable to type 'number'.
23

34

4-
==== tests/cases/conformance/es6/Symbols/symbolProperty47.ts (1 errors) ====
5+
==== tests/cases/conformance/es6/Symbols/symbolProperty47.ts (2 errors) ====
56
class C {
67
get [Symbol.hasInstance]() {
78
return "";
@@ -14,4 +15,6 @@ tests/cases/conformance/es6/Symbols/symbolProperty47.ts(3,16): error TS2322: Typ
1415
}
1516

1617
(new C)[Symbol.hasInstance] = 0;
17-
(new C)[Symbol.hasInstance] = "";
18+
(new C)[Symbol.hasInstance] = "";
19+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
20+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
tests/cases/conformance/es6/Symbols/symbolProperty52.ts(2,13): error TS2339: Property 'nonsense' does not exist on type 'SymbolConstructor'.
22
tests/cases/conformance/es6/Symbols/symbolProperty52.ts(5,1): error TS2322: Type '{}' is not assignable to type '{ [Symbol.nonsense]: number; }'.
33
Property '[Symbol.nonsense]' is missing in type '{}'.
4+
tests/cases/conformance/es6/Symbols/symbolProperty52.ts(7,12): error TS2339: Property 'nonsense' does not exist on type 'SymbolConstructor'.
45

56

6-
==== tests/cases/conformance/es6/Symbols/symbolProperty52.ts (2 errors) ====
7+
==== tests/cases/conformance/es6/Symbols/symbolProperty52.ts (3 errors) ====
78
var obj = {
89
[Symbol.nonsense]: 0
910
~~~~~~~~
@@ -13,4 +14,8 @@ tests/cases/conformance/es6/Symbols/symbolProperty52.ts(5,1): error TS2322: Type
1314
obj = {};
1415
~~~
1516
!!! error TS2322: Type '{}' is not assignable to type '{ [Symbol.nonsense]: number; }'.
16-
!!! error TS2322: Property '[Symbol.nonsense]' is missing in type '{}'.
17+
!!! error TS2322: Property '[Symbol.nonsense]' is missing in type '{}'.
18+
19+
obj[Symbol.nonsense];
20+
~~~~~~~~
21+
!!! error TS2339: Property 'nonsense' does not exist on type 'SymbolConstructor'.

0 commit comments

Comments
 (0)