mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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:
+25
-15
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user