Skip to content

Take runtime semantics of numerically named properties into account. #860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 17, 2014
27 changes: 25 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4663,7 +4663,28 @@ module ts {
}

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

function checkObjectLiteral(node: ObjectLiteral, contextualMapper?: TypeMapper): Type {
Expand Down Expand Up @@ -4713,7 +4734,9 @@ module ts {
if (hasProperty(properties, id)) {
if (kind === IndexKind.String || isNumericName(id)) {
var type = getTypeOfSymbol(properties[id]);
if (!contains(propTypes, type)) propTypes.push(type);
if (!contains(propTypes, type)) {
propTypes.push(type);
}
}
}
}
Expand Down
5 changes: 1 addition & 4 deletions tests/baselines/reference/enumIdentifierLiterals.errors.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
tests/cases/compiler/enumIdentifierLiterals.ts(2,5): error TS1151: An enum member cannot have a numeric name.
tests/cases/compiler/enumIdentifierLiterals.ts(3,5): error TS1151: An enum member cannot have a numeric name.
tests/cases/compiler/enumIdentifierLiterals.ts(4,5): error TS1151: An enum member cannot have a numeric name.
tests/cases/compiler/enumIdentifierLiterals.ts(5,5): error TS1151: An enum member cannot have a numeric name.
tests/cases/compiler/enumIdentifierLiterals.ts(6,5): error TS1151: An enum member cannot have a numeric name.


==== tests/cases/compiler/enumIdentifierLiterals.ts (5 errors) ====
==== tests/cases/compiler/enumIdentifierLiterals.ts (4 errors) ====
enum Nums {
1.0,
~~~
Expand All @@ -17,8 +16,6 @@ tests/cases/compiler/enumIdentifierLiterals.ts(6,5): error TS1151: An enum membe
~~~~~~
!!! error TS1151: An enum member cannot have a numeric name.
"13e-1",
~~~~~~~
!!! error TS1151: An enum member cannot have a numeric name.
0xF00D
~~~~~~
!!! error TS1151: An enum member cannot have a numeric name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(90,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(93,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
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'.
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'.
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'.
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'.
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'.
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'.
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'.
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; }':
Index signatures are incompatible:
Type 'string | number' is not assignable to type 'string':
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(88,9): error TS2304: Cannot find name 'Myn'.


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

interface MyNumber extends Number {
Expand All @@ -40,8 +37,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
!!! error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'.
"3.0": string; // ok
"4.0": number; // error
~~~~~~~~~~~~~~
!!! error TS2412: Property '"4.0"' of type 'number' is not assignable to numeric index type 'string'.
3.0: MyNumber // error
~~~~~~~~~~~~~
!!! error TS2412: Property '3.0' of type 'MyNumber' is not assignable to numeric index type 'string'.
Expand Down Expand Up @@ -87,8 +82,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo
foo(): string; // ok
"3.0": string; // ok
"4.0": number; // error
~~~~~~~~~~~~~~
!!! error TS2412: Property '"4.0"' of type 'number' is not assignable to numeric index type 'string'.
f: MyNumber; // error
}

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
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'.
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'.
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'.
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'.
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'.
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'.
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; }':
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; }':
Index signatures are incompatible:
Type 'string | number | A' is not assignable to type 'A':
Type 'string' is not assignable to type 'A'.
Type 'number | A' is not assignable to type 'A':
Type 'number' is not assignable to type 'A'.


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

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

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

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

// error
var b: { [x: number]: A } = {
~
!!! 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; }':
!!! 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; }':
!!! error TS2322: Index signatures are incompatible:
!!! error TS2322: Type 'string | number | A' is not assignable to type 'A':
!!! error TS2322: Type 'string' is not assignable to type 'A'.
!!! error TS2322: Type 'number | A' is not assignable to type 'A':
!!! error TS2322: Type 'number' is not assignable to type 'A'.
1.0: new A(),
2.0: new B(),
"2.5": new B(),
Expand Down
46 changes: 46 additions & 0 deletions tests/baselines/reference/propertiesAndIndexers2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
tests/cases/compiler/propertiesAndIndexers2.ts(2,5): error TS2413: Numeric index type 'string' is not assignable to string index type 'number'.
tests/cases/compiler/propertiesAndIndexers2.ts(8,5): error TS2411: Property 'c' of type 'string' is not assignable to string index type 'number'.
tests/cases/compiler/propertiesAndIndexers2.ts(9,5): error TS2411: Property '3' of type 'string' is not assignable to string index type 'number'.
tests/cases/compiler/propertiesAndIndexers2.ts(10,5): error TS2411: Property 'Infinity' of type 'string' is not assignable to string index type 'number'.
tests/cases/compiler/propertiesAndIndexers2.ts(11,5): error TS2411: Property '"-Infinity"' of type 'string' is not assignable to string index type 'number'.
tests/cases/compiler/propertiesAndIndexers2.ts(12,5): error TS2411: Property 'NaN' of type 'string' is not assignable to string index type 'number'.
tests/cases/compiler/propertiesAndIndexers2.ts(13,5): error TS2411: Property '"-NaN"' of type 'string' is not assignable to string index type 'number'.
tests/cases/compiler/propertiesAndIndexers2.ts(14,5): error TS2411: Property '6' of type '() => string' is not assignable to string index type 'number'.
tests/cases/compiler/propertiesAndIndexers2.ts(14,5): error TS2412: Property '6' of type '() => string' is not assignable to numeric index type 'string'.


==== tests/cases/compiler/propertiesAndIndexers2.ts (9 errors) ====
interface A {
[n: number]: string;
~~~~~~~~~~~~~~~~~~~~
!!! error TS2413: Numeric index type 'string' is not assignable to string index type 'number'.
[s: string]: number;
}

// All of these should fail.
interface B extends A {
c: string;
~~~~~~~~~~
!!! error TS2411: Property 'c' of type 'string' is not assignable to string index type 'number'.
3: string;
~~~~~~~~~~
!!! error TS2411: Property '3' of type 'string' is not assignable to string index type 'number'.
Infinity: string;
~~~~~~~~~~~~~~~~~
!!! error TS2411: Property 'Infinity' of type 'string' is not assignable to string index type 'number'.
"-Infinity": string;
~~~~~~~~~~~~~~~~~~~~
!!! error TS2411: Property '"-Infinity"' of type 'string' is not assignable to string index type 'number'.
NaN: string;
~~~~~~~~~~~~
!!! error TS2411: Property 'NaN' of type 'string' is not assignable to string index type 'number'.
"-NaN": string;
~~~~~~~~~~~~~~~
!!! error TS2411: Property '"-NaN"' of type 'string' is not assignable to string index type 'number'.
6(): string;
~~~~~~~~~~~~
!!! error TS2411: Property '6' of type '() => string' is not assignable to string index type 'number'.
~~~~~~~~~~~~
!!! error TS2412: Property '6' of type '() => string' is not assignable to numeric index type 'string'.
}

19 changes: 19 additions & 0 deletions tests/baselines/reference/propertiesAndIndexers2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [propertiesAndIndexers2.ts]
interface A {
[n: number]: string;
[s: string]: number;
}

// All of these should fail.
interface B extends A {
c: string;
3: string;
Infinity: string;
"-Infinity": string;
NaN: string;
"-NaN": string;
6(): string;
}


//// [propertiesAndIndexers2.js]
Loading