Skip to content

Commit

Permalink
Support indexing with known symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
JsonFreeman committed Feb 7, 2015
1 parent df826de commit d07ed67
Show file tree
Hide file tree
Showing 26 changed files with 495 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ module ts {
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
var nameExpression = (<ComputedPropertyName>node.name).expression;
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
return "__@" + (<PropertyAccessExpression>nameExpression).name.text;
return getPropertyNameForKnownSymbolName((<PropertyAccessExpression>nameExpression).name.text);
}
return (<Identifier | LiteralExpression>node.name).text;
}
Expand Down
36 changes: 33 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5787,8 +5787,8 @@ module ts {

// See if we can index as a property.
if (node.argumentExpression) {
if (node.argumentExpression.kind === SyntaxKind.StringLiteral || node.argumentExpression.kind === SyntaxKind.NumericLiteral) {
var name = (<LiteralExpression>node.argumentExpression).text;
var name = getPropertyNameForIndexedAccess(node.argumentExpression);
if (name !== undefined) {
var prop = getPropertyOfType(objectType, name);
if (prop) {
getNodeLinks(node).resolvedSymbol = prop;
Expand Down Expand Up @@ -5832,6 +5832,36 @@ module ts {
return unknownType;
}

/**
* If indexArgumentExpression is a string literal or number literal, returns its text.
* If indexArgumentExpression is a well known symbol, returns the property name corresponding
* to this symbol.
* Otherwise, returns undefined.
*/
function getPropertyNameForIndexedAccess(indexArgumentExpression: Expression) {
if (indexArgumentExpression.kind === SyntaxKind.StringLiteral || indexArgumentExpression.kind === SyntaxKind.NumericLiteral) {
return (<LiteralExpression>indexArgumentExpression).text;
}
if (isWellKnownSymbolSyntactically(indexArgumentExpression)) {
var leftHandSide = (<PropertyAccessExpression>indexArgumentExpression).expression;
Debug.assert((<Identifier>leftHandSide).text === "Symbol");
// The name is Symbol.<someName>, so make sure Symbol actually resolves to the
// global Symbol object
var leftHandSideSymbol = resolveName(indexArgumentExpression, (<Identifier>leftHandSide).text,
SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
if (leftHandSideSymbol === globalESSymbolConstructorSymbol) {
// Make sure the property type is the primitive symbol type
var rightHandSideName = (<Identifier>(<PropertyAccessExpression>indexArgumentExpression).name).text;
var esSymbolConstructorPropertyType = getTypeOfPropertyOfType(globalESSymbolConstructorType, rightHandSideName);
if (esSymbolConstructorPropertyType && esSymbolConstructorPropertyType.flags & TypeFlags.ESSymbol) {

This comment has been minimized.

Copy link
@yuit

yuit Feb 10, 2015

Contributor

Would prefer parenthesis around esSymbolConstructorPropertyType.flags & TypeFlags.ESSymbol

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Feb 10, 2015

Author Contributor

Okay

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Feb 10, 2015

Author Contributor

Actually this doesn't even exist anymore

return getPropertyNameForKnownSymbolName(rightHandSideName);
}
}
}

return undefined;
}

function resolveUntypedCall(node: CallLikeExpression): Signature {
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
checkExpression((<TaggedTemplateExpression>node).template);
Expand Down Expand Up @@ -10334,7 +10364,7 @@ module ts {
globalTemplateStringsArrayType = getGlobalType("TemplateStringsArray");
globalESSymbolType = getGlobalType("Symbol");
globalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol");
globalESSymbolConstructorType = getTypeOfGlobalSymbol(globalESSymbolConstructorSymbol, /*arity*/ 0);
globalESSymbolConstructorType = getTypeOfSymbol(globalESSymbolConstructorSymbol);
}
else {
globalTemplateStringsArrayType = unknownType;
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,10 @@ module ts {
return node.kind === SyntaxKind.PropertyAccessExpression && isESSymbolIdentifier((<PropertyAccessExpression>node).expression);
}

export function getPropertyNameForKnownSymbolName(symbolName: string): string {
return "__@" + symbolName;
}

/**
* Includes the word "Symbol" with unicode escapes
*/
Expand Down
27 changes: 27 additions & 0 deletions tests/baselines/reference/symbolProperty17.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/conformance/es6/Symbols/symbolProperty17.ts ===
interface I {
>I : I

[Symbol.iterator]: number;
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

[s: symbol]: string;
>s : symbol

"__@iterator": string;
}

var i: I;
>i : I
>I : I

var it = i[Symbol.iterator];
>it : number
>i[Symbol.iterator] : number
>i : I
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

47 changes: 47 additions & 0 deletions tests/baselines/reference/symbolProperty18.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
=== tests/cases/conformance/es6/Symbols/symbolProperty18.ts ===
var i = {
>i : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
>{ [Symbol.iterator]: 0, [Symbol.toStringTag]() { return "" }, set [Symbol.toPrimitive](p: boolean) { }} : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }

[Symbol.iterator]: 0,
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

[Symbol.toStringTag]() { return "" },
>Symbol.toStringTag : symbol
>Symbol : SymbolConstructor
>toStringTag : symbol

set [Symbol.toPrimitive](p: boolean) { }
>Symbol.toPrimitive : symbol
>Symbol : SymbolConstructor
>toPrimitive : symbol
>p : boolean
}

var it = i[Symbol.iterator];
>it : number
>i[Symbol.iterator] : number
>i : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

var str = i[Symbol.toStringTag]();
>str : string
>i[Symbol.toStringTag]() : string
>i[Symbol.toStringTag] : () => string
>i : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
>Symbol.toStringTag : symbol
>Symbol : SymbolConstructor
>toStringTag : symbol

i[Symbol.toPrimitive] = false;
>i[Symbol.toPrimitive] = false : boolean
>i[Symbol.toPrimitive] : boolean
>i : { [Symbol.iterator]: number; [Symbol.toStringTag](): string; [Symbol.toPrimitive]: boolean; }
>Symbol.toPrimitive : symbol
>Symbol : SymbolConstructor
>toPrimitive : symbol

38 changes: 38 additions & 0 deletions tests/baselines/reference/symbolProperty19.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
=== tests/cases/conformance/es6/Symbols/symbolProperty19.ts ===
var i = {
>i : { [Symbol.iterator]: { p: any; }; [Symbol.toStringTag](): { p: any; }; }
>{ [Symbol.iterator]: { p: null }, [Symbol.toStringTag]() { return { p: undefined }; }} : { [Symbol.iterator]: { p: null; }; [Symbol.toStringTag](): { p: any; }; }

[Symbol.iterator]: { p: null },
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol
>{ p: null } : { p: null; }
>p : null

[Symbol.toStringTag]() { return { p: undefined }; }
>Symbol.toStringTag : symbol
>Symbol : SymbolConstructor
>toStringTag : symbol
>{ p: undefined } : { p: undefined; }
>p : undefined
>undefined : undefined
}

var it = i[Symbol.iterator];
>it : { p: any; }
>i[Symbol.iterator] : { p: any; }
>i : { [Symbol.iterator]: { p: any; }; [Symbol.toStringTag](): { p: any; }; }
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

var str = i[Symbol.toStringTag]();
>str : { p: any; }
>i[Symbol.toStringTag]() : { p: any; }
>i[Symbol.toStringTag] : () => { p: any; }
>i : { [Symbol.iterator]: { p: any; }; [Symbol.toStringTag](): { p: any; }; }
>Symbol.toStringTag : symbol
>Symbol : SymbolConstructor
>toStringTag : symbol

34 changes: 34 additions & 0 deletions tests/baselines/reference/symbolProperty28.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
=== tests/cases/conformance/es6/Symbols/symbolProperty28.ts ===
class C1 {
>C1 : C1

[Symbol.toStringTag]() {
>Symbol.toStringTag : symbol
>Symbol : SymbolConstructor
>toStringTag : symbol

return { x: "" };
>{ x: "" } : { x: string; }
>x : string
}
}

class C2 extends C1 { }
>C2 : C2
>C1 : C1

var c: C2;
>c : C2
>C2 : C2

var obj = c[Symbol.toStringTag]().x;
>obj : string
>c[Symbol.toStringTag]().x : string
>c[Symbol.toStringTag]() : { x: string; }
>c[Symbol.toStringTag] : () => { x: string; }
>c : C2
>Symbol.toStringTag : symbol
>Symbol : SymbolConstructor
>toStringTag : symbol
>x : string

48 changes: 48 additions & 0 deletions tests/baselines/reference/symbolProperty40.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
=== tests/cases/conformance/es6/Symbols/symbolProperty40.ts ===
class C {
>C : C

[Symbol.iterator](x: string): string;
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol
>x : string

[Symbol.iterator](x: number): number;
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol
>x : number

[Symbol.iterator](x: any) {
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol
>x : any

return undefined;
>undefined : undefined
}
}

var c = new C;
>c : C
>new C : C
>C : typeof C

c[Symbol.iterator]("");
>c[Symbol.iterator]("") : string
>c[Symbol.iterator] : { (x: string): string; (x: number): number; }
>c : C
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

c[Symbol.iterator](0);
>c[Symbol.iterator](0) : number
>c[Symbol.iterator] : { (x: string): string; (x: number): number; }
>c : C
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

51 changes: 51 additions & 0 deletions tests/baselines/reference/symbolProperty41.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
=== tests/cases/conformance/es6/Symbols/symbolProperty41.ts ===
class C {
>C : C

[Symbol.iterator](x: string): { x: string };
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol
>x : string
>x : string

[Symbol.iterator](x: "hello"): { x: string; hello: string };
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol
>x : "hello"
>x : string
>hello : string

[Symbol.iterator](x: any) {
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol
>x : any

return undefined;
>undefined : undefined
}
}

var c = new C;
>c : C
>new C : C
>C : typeof C

c[Symbol.iterator]("");
>c[Symbol.iterator]("") : { x: string; }
>c[Symbol.iterator] : { (x: string): { x: string; }; (x: "hello"): { x: string; hello: string; }; }
>c : C
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

c[Symbol.iterator]("hello");
>c[Symbol.iterator]("hello") : { x: string; hello: string; }
>c[Symbol.iterator] : { (x: string): { x: string; }; (x: "hello"): { x: string; hello: string; }; }
>c : C
>Symbol.iterator : symbol
>Symbol : SymbolConstructor
>iterator : symbol

17 changes: 17 additions & 0 deletions tests/baselines/reference/symbolProperty46.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
tests/cases/conformance/es6/Symbols/symbolProperty46.ts(10,1): error TS2322: Type 'number' is not assignable to type 'string'.


==== tests/cases/conformance/es6/Symbols/symbolProperty46.ts (1 errors) ====
class C {
get [Symbol.hasInstance]() {
return "";
}
// Should take a string
set [Symbol.hasInstance](x) {
}
}

(new C)[Symbol.hasInstance] = 0;
~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
(new C)[Symbol.hasInstance] = "";
7 changes: 5 additions & 2 deletions tests/baselines/reference/symbolProperty47.errors.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
tests/cases/conformance/es6/Symbols/symbolProperty47.ts(3,16): error TS2322: Type 'string' is not assignable to type 'number'.
tests/cases/conformance/es6/Symbols/symbolProperty47.ts(11,1): error TS2322: Type 'string' is not assignable to type 'number'.


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

(new C)[Symbol.hasInstance] = 0;
(new C)[Symbol.hasInstance] = "";
(new C)[Symbol.hasInstance] = "";
~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'number'.
9 changes: 7 additions & 2 deletions tests/baselines/reference/symbolProperty52.errors.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
tests/cases/conformance/es6/Symbols/symbolProperty52.ts(2,13): error TS2339: Property 'nonsense' does not exist on type 'SymbolConstructor'.
tests/cases/conformance/es6/Symbols/symbolProperty52.ts(5,1): error TS2322: Type '{}' is not assignable to type '{ [Symbol.nonsense]: number; }'.
Property '[Symbol.nonsense]' is missing in type '{}'.
tests/cases/conformance/es6/Symbols/symbolProperty52.ts(7,12): error TS2339: Property 'nonsense' does not exist on type 'SymbolConstructor'.


==== tests/cases/conformance/es6/Symbols/symbolProperty52.ts (2 errors) ====
==== tests/cases/conformance/es6/Symbols/symbolProperty52.ts (3 errors) ====
var obj = {
[Symbol.nonsense]: 0
~~~~~~~~
Expand All @@ -13,4 +14,8 @@ tests/cases/conformance/es6/Symbols/symbolProperty52.ts(5,1): error TS2322: Type
obj = {};
~~~
!!! error TS2322: Type '{}' is not assignable to type '{ [Symbol.nonsense]: number; }'.
!!! error TS2322: Property '[Symbol.nonsense]' is missing in type '{}'.
!!! error TS2322: Property '[Symbol.nonsense]' is missing in type '{}'.

obj[Symbol.nonsense];
~~~~~~~~
!!! error TS2339: Property 'nonsense' does not exist on type 'SymbolConstructor'.
Loading

0 comments on commit d07ed67

Please sign in to comment.