Skip to content

Commit ad6203f

Browse files
Merge pull request #860 from Microsoft/numbersAreHard
Take runtime semantics of numerically named properties into account.
2 parents 2cef9ab + 73d575e commit ad6203f

10 files changed

+308
-32
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4675,7 +4675,28 @@ module ts {
46754675
}
46764676

46774677
function isNumericName(name: string) {
4678-
return (name !== "") && !isNaN(<number><any>name);
4678+
// The intent of numeric names is that
4679+
// - they are names with text in a numeric form, and that
4680+
// - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit',
4681+
// acquired by applying the abstract 'ToNumber' operation on the name's text.
4682+
//
4683+
// The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name.
4684+
// In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold.
4685+
//
4686+
// Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)'
4687+
// according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'.
4688+
// Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names
4689+
// because their 'ToString' representation is not equal to their original text.
4690+
// This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1.
4691+
//
4692+
// Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'.
4693+
// The '+' prefix operator is equivalent here to applying the abstract ToNumber operation.
4694+
// Applying the 'toString()' method on a number gives us the abstract ToString operation on a number.
4695+
//
4696+
// Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional.
4697+
// This is desired behavior, because when indexing with them as numeric entities, you are indexing
4698+
// with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively.
4699+
return (+name).toString() === name;
46794700
}
46804701

46814702
function checkObjectLiteral(node: ObjectLiteral, contextualMapper?: TypeMapper): Type {
@@ -4725,7 +4746,9 @@ module ts {
47254746
if (hasProperty(properties, id)) {
47264747
if (kind === IndexKind.String || isNumericName(id)) {
47274748
var type = getTypeOfSymbol(properties[id]);
4728-
if (!contains(propTypes, type)) propTypes.push(type);
4749+
if (!contains(propTypes, type)) {
4750+
propTypes.push(type);
4751+
}
47294752
}
47304753
}
47314754
}

tests/baselines/reference/enumIdentifierLiterals.errors.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
tests/cases/compiler/enumIdentifierLiterals.ts(2,5): error TS1151: An enum member cannot have a numeric name.
22
tests/cases/compiler/enumIdentifierLiterals.ts(3,5): error TS1151: An enum member cannot have a numeric name.
33
tests/cases/compiler/enumIdentifierLiterals.ts(4,5): error TS1151: An enum member cannot have a numeric name.
4-
tests/cases/compiler/enumIdentifierLiterals.ts(5,5): error TS1151: An enum member cannot have a numeric name.
54
tests/cases/compiler/enumIdentifierLiterals.ts(6,5): error TS1151: An enum member cannot have a numeric name.
65

76

8-
==== tests/cases/compiler/enumIdentifierLiterals.ts (5 errors) ====
7+
==== tests/cases/compiler/enumIdentifierLiterals.ts (4 errors) ====
98
enum Nums {
109
1.0,
1110
~~~
@@ -17,8 +16,6 @@ tests/cases/compiler/enumIdentifierLiterals.ts(6,5): error TS1151: An enum membe
1716
~~~~~~
1817
!!! error TS1151: An enum member cannot have a numeric name.
1918
"13e-1",
20-
~~~~~~~
21-
!!! error TS1151: An enum member cannot have a numeric name.
2219
0xF00D
2320
~~~~~~
2421
!!! error TS1151: An enum member cannot have a numeric name.

tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,17 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
44
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(90,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
55
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(93,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
66
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(18,5): error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'.
7-
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(20,5): error TS2412: Property '"4.0"' of type 'number' is not assignable to numeric index type 'string'.
87
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(21,5): error TS2412: Property '3.0' of type 'MyNumber' is not assignable to numeric index type 'string'.
98
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(50,5): error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'.
10-
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(55,5): error TS2412: Property '"4.0"' of type 'number' is not assignable to numeric index type 'string'.
119
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(68,5): error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'.
12-
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(73,5): error TS2412: Property '"4.0"' of type 'number' is not assignable to numeric index type 'string'.
1310
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(78,5): error TS2322: Type '{ [x: number]: string | number; 1.0: string; 2.0: number; a: string; b: number; c: () => void; "d": string; "e": number; "3.0": string; "4.0": number; f: any; X: string; foo: () => string; }' is not assignable to type '{ [x: number]: string; }':
1411
Index signatures are incompatible:
1512
Type 'string | number' is not assignable to type 'string':
1613
Type 'number' is not assignable to type 'string'.
1714
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(88,9): error TS2304: Cannot find name 'Myn'.
1815

1916

20-
==== tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts (14 errors) ====
17+
==== tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts (11 errors) ====
2118
// String indexer types constrain the types of named properties in their containing type
2219

2320
interface MyNumber extends Number {
@@ -40,8 +37,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
4037
!!! error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'.
4138
"3.0": string; // ok
4239
"4.0": number; // error
43-
~~~~~~~~~~~~~~
44-
!!! error TS2412: Property '"4.0"' of type 'number' is not assignable to numeric index type 'string'.
4540
3.0: MyNumber // error
4641
~~~~~~~~~~~~~
4742
!!! error TS2412: Property '3.0' of type 'MyNumber' is not assignable to numeric index type 'string'.
@@ -87,8 +82,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
8782
foo(): string; // ok
8883
"3.0": string; // ok
8984
"4.0": number; // error
90-
~~~~~~~~~~~~~~
91-
!!! error TS2412: Property '"4.0"' of type 'number' is not assignable to numeric index type 'string'.
9285
f: MyNumber; // error
9386
}
9487

@@ -109,8 +102,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
109102
foo(): string; // ok
110103
"3.0": string; // ok
111104
"4.0": number; // error
112-
~~~~~~~~~~~~~~
113-
!!! error TS2412: Property '"4.0"' of type 'number' is not assignable to numeric index type 'string'.
114105
f: MyNumber; // error
115106
}
116107

tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations2.errors.txt

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(16,5): error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'.
2-
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(17,5): error TS2412: Property '"4.0"' of type 'string' is not assignable to numeric index type 'A'.
32
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(25,5): error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'.
4-
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(26,5): error TS2412: Property '"4.0"' of type 'string' is not assignable to numeric index type 'A'.
53
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(34,5): error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'.
6-
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(35,5): error TS2412: Property '"4.0"' of type 'string' is not assignable to numeric index type 'A'.
7-
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(39,5): error TS2322: Type '{ [x: number]: string | number | A; 1.0: A; 2.0: B; 3.0: number; "2.5": B; "4.0": string; }' is not assignable to type '{ [x: number]: A; }':
4+
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(39,5): error TS2322: Type '{ [x: number]: number | A; 1.0: A; 2.0: B; 3.0: number; "2.5": B; "4.0": string; }' is not assignable to type '{ [x: number]: A; }':
85
Index signatures are incompatible:
9-
Type 'string | number | A' is not assignable to type 'A':
10-
Type 'string' is not assignable to type 'A'.
6+
Type 'number | A' is not assignable to type 'A':
7+
Type 'number' is not assignable to type 'A'.
118

129

13-
==== tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts (7 errors) ====
10+
==== tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts (4 errors) ====
1411
// String indexer providing a constraint of a user defined type
1512

1613
class A {
@@ -30,8 +27,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
3027
~~~~~~~~~~~~
3128
!!! error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'.
3229
"4.0": string; // error
33-
~~~~~~~~~~~~~~
34-
!!! error TS2412: Property '"4.0"' of type 'string' is not assignable to numeric index type 'A'.
3530
}
3631

3732
interface Foo2 {
@@ -43,8 +38,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
4338
~~~~~~~~~~~~
4439
!!! error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'.
4540
"4.0": string; // error
46-
~~~~~~~~~~~~~~
47-
!!! error TS2412: Property '"4.0"' of type 'string' is not assignable to numeric index type 'A'.
4841
}
4942

5043
var a: {
@@ -56,17 +49,15 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
5649
~~~~~~~~~~~~
5750
!!! error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'.
5851
"4.0": string; // error
59-
~~~~~~~~~~~~~~
60-
!!! error TS2412: Property '"4.0"' of type 'string' is not assignable to numeric index type 'A'.
6152
};
6253

6354
// error
6455
var b: { [x: number]: A } = {
6556
~
66-
!!! error TS2322: Type '{ [x: number]: string | number | A; 1.0: A; 2.0: B; 3.0: number; "2.5": B; "4.0": string; }' is not assignable to type '{ [x: number]: A; }':
57+
!!! error TS2322: Type '{ [x: number]: number | A; 1.0: A; 2.0: B; 3.0: number; "2.5": B; "4.0": string; }' is not assignable to type '{ [x: number]: A; }':
6758
!!! error TS2322: Index signatures are incompatible:
68-
!!! error TS2322: Type 'string | number | A' is not assignable to type 'A':
69-
!!! error TS2322: Type 'string' is not assignable to type 'A'.
59+
!!! error TS2322: Type 'number | A' is not assignable to type 'A':
60+
!!! error TS2322: Type 'number' is not assignable to type 'A'.
7061
1.0: new A(),
7162
2.0: new B(),
7263
"2.5": new B(),
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
tests/cases/compiler/propertiesAndIndexers2.ts(2,5): error TS2413: Numeric index type 'string' is not assignable to string index type 'number'.
2+
tests/cases/compiler/propertiesAndIndexers2.ts(8,5): error TS2411: Property 'c' of type 'string' is not assignable to string index type 'number'.
3+
tests/cases/compiler/propertiesAndIndexers2.ts(9,5): error TS2411: Property '3' of type 'string' is not assignable to string index type 'number'.
4+
tests/cases/compiler/propertiesAndIndexers2.ts(10,5): error TS2411: Property 'Infinity' of type 'string' is not assignable to string index type 'number'.
5+
tests/cases/compiler/propertiesAndIndexers2.ts(11,5): error TS2411: Property '"-Infinity"' of type 'string' is not assignable to string index type 'number'.
6+
tests/cases/compiler/propertiesAndIndexers2.ts(12,5): error TS2411: Property 'NaN' of type 'string' is not assignable to string index type 'number'.
7+
tests/cases/compiler/propertiesAndIndexers2.ts(13,5): error TS2411: Property '"-NaN"' of type 'string' is not assignable to string index type 'number'.
8+
tests/cases/compiler/propertiesAndIndexers2.ts(14,5): error TS2411: Property '6' of type '() => string' is not assignable to string index type 'number'.
9+
tests/cases/compiler/propertiesAndIndexers2.ts(14,5): error TS2412: Property '6' of type '() => string' is not assignable to numeric index type 'string'.
10+
11+
12+
==== tests/cases/compiler/propertiesAndIndexers2.ts (9 errors) ====
13+
interface A {
14+
[n: number]: string;
15+
~~~~~~~~~~~~~~~~~~~~
16+
!!! error TS2413: Numeric index type 'string' is not assignable to string index type 'number'.
17+
[s: string]: number;
18+
}
19+
20+
// All of these should fail.
21+
interface B extends A {
22+
c: string;
23+
~~~~~~~~~~
24+
!!! error TS2411: Property 'c' of type 'string' is not assignable to string index type 'number'.
25+
3: string;
26+
~~~~~~~~~~
27+
!!! error TS2411: Property '3' of type 'string' is not assignable to string index type 'number'.
28+
Infinity: string;
29+
~~~~~~~~~~~~~~~~~
30+
!!! error TS2411: Property 'Infinity' of type 'string' is not assignable to string index type 'number'.
31+
"-Infinity": string;
32+
~~~~~~~~~~~~~~~~~~~~
33+
!!! error TS2411: Property '"-Infinity"' of type 'string' is not assignable to string index type 'number'.
34+
NaN: string;
35+
~~~~~~~~~~~~
36+
!!! error TS2411: Property 'NaN' of type 'string' is not assignable to string index type 'number'.
37+
"-NaN": string;
38+
~~~~~~~~~~~~~~~
39+
!!! error TS2411: Property '"-NaN"' of type 'string' is not assignable to string index type 'number'.
40+
6(): string;
41+
~~~~~~~~~~~~
42+
!!! error TS2411: Property '6' of type '() => string' is not assignable to string index type 'number'.
43+
~~~~~~~~~~~~
44+
!!! error TS2412: Property '6' of type '() => string' is not assignable to numeric index type 'string'.
45+
}
46+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [propertiesAndIndexers2.ts]
2+
interface A {
3+
[n: number]: string;
4+
[s: string]: number;
5+
}
6+
7+
// All of these should fail.
8+
interface B extends A {
9+
c: string;
10+
3: string;
11+
Infinity: string;
12+
"-Infinity": string;
13+
NaN: string;
14+
"-NaN": string;
15+
6(): string;
16+
}
17+
18+
19+
//// [propertiesAndIndexers2.js]

0 commit comments

Comments
 (0)