Merge pull request #5267 from Microsoft/disallow-primitive-assignability-to-index-of-any

Disallow primitive assignability to indexer of type any
This commit is contained in:
Nathan Shively-Sanders
2015-10-19 15:48:04 -07:00
6 changed files with 66 additions and 18 deletions
+25 -15
View File
@@ -4897,7 +4897,7 @@ namespace ts {
if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) {
// Report structural errors only if we haven't reported any errors yet
let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
if (result = objectTypeRelatedTo(apparentType, <ObjectType>target, reportStructuralErrors)) {
if (result = objectTypeRelatedTo(apparentType, source, target, reportStructuralErrors)) {
errorInfo = saveErrorInfo;
return result;
}
@@ -4919,7 +4919,7 @@ namespace ts {
return result;
}
}
return objectTypeRelatedTo(<ObjectType>source, <ObjectType>target, /*reportErrors*/ false);
return objectTypeRelatedTo(source, source, target, /*reportErrors*/ false);
}
if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
return typeParameterIdenticalTo(<TypeParameter>source, <TypeParameter>target);
@@ -5073,11 +5073,11 @@ namespace ts {
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
// equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion
// and issue an error. Otherwise, actually compare the structure of the two types.
function objectTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
function objectTypeRelatedTo(apparentSource: Type, originalSource: Type, target: Type, reportErrors: boolean): Ternary {
if (overflow) {
return Ternary.False;
}
let id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
let id = relation !== identityRelation || apparentSource.id < target.id ? apparentSource.id + "," + target.id : target.id + "," + apparentSource.id;
let related = relation[id];
if (related !== undefined) {
// If we computed this relation already and it was failed and reported, or if we're not being asked to elaborate
@@ -5104,28 +5104,28 @@ namespace ts {
maybeStack = [];
expandingFlags = 0;
}
sourceStack[depth] = source;
sourceStack[depth] = apparentSource;
targetStack[depth] = target;
maybeStack[depth] = {};
maybeStack[depth][id] = RelationComparisonResult.Succeeded;
depth++;
let saveExpandingFlags = expandingFlags;
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth)) expandingFlags |= 1;
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(apparentSource, sourceStack, depth)) expandingFlags |= 1;
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack, depth)) expandingFlags |= 2;
let result: Ternary;
if (expandingFlags === 3) {
result = Ternary.Maybe;
}
else {
result = propertiesRelatedTo(source, target, reportErrors);
result = propertiesRelatedTo(apparentSource, target, reportErrors);
if (result) {
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportErrors);
result &= signaturesRelatedTo(apparentSource, target, SignatureKind.Call, reportErrors);
if (result) {
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportErrors);
result &= signaturesRelatedTo(apparentSource, target, SignatureKind.Construct, reportErrors);
if (result) {
result &= stringIndexTypesRelatedTo(source, target, reportErrors);
result &= stringIndexTypesRelatedTo(apparentSource, originalSource, target, reportErrors);
if (result) {
result &= numberIndexTypesRelatedTo(source, target, reportErrors);
result &= numberIndexTypesRelatedTo(apparentSource, originalSource, target, reportErrors);
}
}
}
@@ -5456,12 +5456,17 @@ namespace ts {
return result;
}
function stringIndexTypesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
function stringIndexTypesRelatedTo(source: Type, originalSource: Type, target: Type, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return indexTypesIdenticalTo(IndexKind.String, source, target);
}
let targetType = getIndexTypeOfType(target, IndexKind.String);
if (targetType && !(targetType.flags & TypeFlags.Any)) {
if (targetType) {
if ((targetType.flags & TypeFlags.Any) && !(originalSource.flags & TypeFlags.Primitive)) {
// non-primitive assignment to any is always allowed, eg
// `var x: { [index: string]: any } = { property: 12 };`
return Ternary.True;
}
let sourceType = getIndexTypeOfType(source, IndexKind.String);
if (!sourceType) {
if (reportErrors) {
@@ -5481,12 +5486,17 @@ namespace ts {
return Ternary.True;
}
function numberIndexTypesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
function numberIndexTypesRelatedTo(source: Type, originalSource: Type, target: Type, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return indexTypesIdenticalTo(IndexKind.Number, source, target);
}
let targetType = getIndexTypeOfType(target, IndexKind.Number);
if (targetType && !(targetType.flags & TypeFlags.Any)) {
if (targetType) {
if ((targetType.flags & TypeFlags.Any) && !(originalSource.flags & TypeFlags.Primitive)) {
// non-primitive assignment to any is always allowed, eg
// `var x: { [index: number]: any } = { property: 12 };`
return Ternary.True;
}
let sourceStringType = getIndexTypeOfType(source, IndexKind.String);
let sourceNumberType = getIndexTypeOfType(source, IndexKind.Number);
if (!(sourceStringType || sourceNumberType)) {
@@ -2,9 +2,13 @@ tests/cases/compiler/assignmentCompat1.ts(4,1): error TS2322: Type '{ [index: st
Property 'one' is missing in type '{ [index: string]: any; }'.
tests/cases/compiler/assignmentCompat1.ts(6,1): error TS2322: Type '{ [index: number]: any; }' is not assignable to type '{ one: number; }'.
Property 'one' is missing in type '{ [index: number]: any; }'.
tests/cases/compiler/assignmentCompat1.ts(8,1): error TS2322: Type 'string' is not assignable to type '{ [index: string]: any; }'.
Index signature is missing in type 'String'.
tests/cases/compiler/assignmentCompat1.ts(10,1): error TS2322: Type 'boolean' is not assignable to type '{ [index: number]: any; }'.
Index signature is missing in type 'Boolean'.
==== tests/cases/compiler/assignmentCompat1.ts (2 errors) ====
==== tests/cases/compiler/assignmentCompat1.ts (4 errors) ====
var x = { one: 1 };
var y: { [index: string]: any };
var z: { [index: number]: any };
@@ -18,4 +22,14 @@ tests/cases/compiler/assignmentCompat1.ts(6,1): error TS2322: Type '{ [index: nu
!!! error TS2322: Type '{ [index: number]: any; }' is not assignable to type '{ one: number; }'.
!!! error TS2322: Property 'one' is missing in type '{ [index: number]: any; }'.
z = x; // Ok because index signature type is any
y = "foo"; // Error
~
!!! error TS2322: Type 'string' is not assignable to type '{ [index: string]: any; }'.
!!! error TS2322: Index signature is missing in type 'String'.
z = "foo"; // OK, string has numeric indexer
z = false; // Error
~
!!! error TS2322: Type 'boolean' is not assignable to type '{ [index: number]: any; }'.
!!! error TS2322: Index signature is missing in type 'Boolean'.
@@ -6,6 +6,10 @@ x = y; // Error
y = x; // Ok because index signature type is any
x = z; // Error
z = x; // Ok because index signature type is any
y = "foo"; // Error
z = "foo"; // OK, string has numeric indexer
z = false; // Error
//// [assignmentCompat1.js]
@@ -16,3 +20,6 @@ x = y; // Error
y = x; // Ok because index signature type is any
x = z; // Error
z = x; // Ok because index signature type is any
y = "foo"; // Error
z = "foo"; // OK, string has numeric indexer
z = false; // Error
@@ -1,13 +1,14 @@
tests/cases/compiler/indexTypeCheck.ts(2,2): error TS1021: An index signature must have a type annotation.
tests/cases/compiler/indexTypeCheck.ts(3,2): error TS1021: An index signature must have a type annotation.
tests/cases/compiler/indexTypeCheck.ts(17,2): error TS2413: Numeric index type 'number' is not assignable to string index type 'string'.
tests/cases/compiler/indexTypeCheck.ts(22,2): error TS2413: Numeric index type 'Orange' is not assignable to string index type 'Yellow'.
tests/cases/compiler/indexTypeCheck.ts(27,2): error TS2413: Numeric index type 'number' is not assignable to string index type 'string'.
tests/cases/compiler/indexTypeCheck.ts(32,3): error TS1096: An index signature must have exactly one parameter.
tests/cases/compiler/indexTypeCheck.ts(36,3): error TS1023: An index signature parameter type must be 'string' or 'number'.
tests/cases/compiler/indexTypeCheck.ts(51,1): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'.
==== tests/cases/compiler/indexTypeCheck.ts (7 errors) ====
==== tests/cases/compiler/indexTypeCheck.ts (8 errors) ====
interface Red {
[n:number]; // ok
~~~~~~~~~~~
@@ -36,6 +37,8 @@ tests/cases/compiler/indexTypeCheck.ts(51,1): error TS2342: An index expression
interface Green {
[n:number]: Orange; // error
~~~~~~~~~~~~~~~~~~~
!!! error TS2413: Numeric index type 'Orange' is not assignable to string index type 'Yellow'.
[s:string]: Yellow; // ok
}
@@ -30,6 +30,8 @@ tests/cases/compiler/intTypeCheck.ts(134,21): error TS1109: Expression expected.
tests/cases/compiler/intTypeCheck.ts(134,22): error TS2304: Cannot find name 'i3'.
tests/cases/compiler/intTypeCheck.ts(135,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
tests/cases/compiler/intTypeCheck.ts(142,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
tests/cases/compiler/intTypeCheck.ts(148,5): error TS2322: Type 'boolean' is not assignable to type 'i4'.
Index signature is missing in type 'Boolean'.
tests/cases/compiler/intTypeCheck.ts(148,21): error TS1109: Expression expected.
tests/cases/compiler/intTypeCheck.ts(148,22): error TS2304: Cannot find name 'i4'.
tests/cases/compiler/intTypeCheck.ts(149,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
@@ -66,12 +68,14 @@ tests/cases/compiler/intTypeCheck.ts(190,21): error TS1109: Expression expected.
tests/cases/compiler/intTypeCheck.ts(190,22): error TS2304: Cannot find name 'i7'.
tests/cases/compiler/intTypeCheck.ts(191,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
tests/cases/compiler/intTypeCheck.ts(198,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
tests/cases/compiler/intTypeCheck.ts(204,5): error TS2322: Type 'boolean' is not assignable to type 'i8'.
Index signature is missing in type 'Boolean'.
tests/cases/compiler/intTypeCheck.ts(204,21): error TS1109: Expression expected.
tests/cases/compiler/intTypeCheck.ts(204,22): error TS2304: Cannot find name 'i8'.
tests/cases/compiler/intTypeCheck.ts(205,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
==== tests/cases/compiler/intTypeCheck.ts (61 errors) ====
==== tests/cases/compiler/intTypeCheck.ts (63 errors) ====
interface i1 {
//Property Signatures
p;
@@ -280,6 +284,9 @@ tests/cases/compiler/intTypeCheck.ts(205,17): error TS2351: Cannot use 'new' wit
//var obj40: i4 = function foo() { };
var obj41: i4 = <i4> anyVar;
var obj42: i4 = new <i4> anyVar;
~~~~~
!!! error TS2322: Type 'boolean' is not assignable to type 'i4'.
!!! error TS2322: Index signature is missing in type 'Boolean'.
~
!!! error TS1109: Expression expected.
~~
@@ -402,6 +409,9 @@ tests/cases/compiler/intTypeCheck.ts(205,17): error TS2351: Cannot use 'new' wit
//var obj84: i8 = function foo() { };
var obj85: i8 = <i8> anyVar;
var obj86: i8 = new <i8> anyVar;
~~~~~
!!! error TS2322: Type 'boolean' is not assignable to type 'i8'.
!!! error TS2322: Index signature is missing in type 'Boolean'.
~
!!! error TS1109: Expression expected.
~~
@@ -5,3 +5,7 @@ x = y; // Error
y = x; // Ok because index signature type is any
x = z; // Error
z = x; // Ok because index signature type is any
y = "foo"; // Error
z = "foo"; // OK, string has numeric indexer
z = false; // Error