Skip to content

Commit f9c40d1

Browse files
committed
Merge pull request #5267 from Microsoft/disallow-primitive-assignability-to-index-of-any
Disallow primitive assignability to indexer of type any
2 parents 683eec0 + 5cd0ca1 commit f9c40d1

File tree

6 files changed

+66
-18
lines changed

6 files changed

+66
-18
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4897,7 +4897,7 @@ namespace ts {
48974897
if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) {
48984898
// Report structural errors only if we haven't reported any errors yet
48994899
let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
4900-
if (result = objectTypeRelatedTo(apparentType, <ObjectType>target, reportStructuralErrors)) {
4900+
if (result = objectTypeRelatedTo(apparentType, source, target, reportStructuralErrors)) {
49014901
errorInfo = saveErrorInfo;
49024902
return result;
49034903
}
@@ -4919,7 +4919,7 @@ namespace ts {
49194919
return result;
49204920
}
49214921
}
4922-
return objectTypeRelatedTo(<ObjectType>source, <ObjectType>target, /*reportErrors*/ false);
4922+
return objectTypeRelatedTo(source, source, target, /*reportErrors*/ false);
49234923
}
49244924
if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
49254925
return typeParameterIdenticalTo(<TypeParameter>source, <TypeParameter>target);
@@ -5073,11 +5073,11 @@ namespace ts {
50735073
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
50745074
// equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion
50755075
// and issue an error. Otherwise, actually compare the structure of the two types.
5076-
function objectTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
5076+
function objectTypeRelatedTo(apparentSource: Type, originalSource: Type, target: Type, reportErrors: boolean): Ternary {
50775077
if (overflow) {
50785078
return Ternary.False;
50795079
}
5080-
let id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
5080+
let id = relation !== identityRelation || apparentSource.id < target.id ? apparentSource.id + "," + target.id : target.id + "," + apparentSource.id;
50815081
let related = relation[id];
50825082
if (related !== undefined) {
50835083
// 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 {
51045104
maybeStack = [];
51055105
expandingFlags = 0;
51065106
}
5107-
sourceStack[depth] = source;
5107+
sourceStack[depth] = apparentSource;
51085108
targetStack[depth] = target;
51095109
maybeStack[depth] = {};
51105110
maybeStack[depth][id] = RelationComparisonResult.Succeeded;
51115111
depth++;
51125112
let saveExpandingFlags = expandingFlags;
5113-
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth)) expandingFlags |= 1;
5113+
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(apparentSource, sourceStack, depth)) expandingFlags |= 1;
51145114
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack, depth)) expandingFlags |= 2;
51155115
let result: Ternary;
51165116
if (expandingFlags === 3) {
51175117
result = Ternary.Maybe;
51185118
}
51195119
else {
5120-
result = propertiesRelatedTo(source, target, reportErrors);
5120+
result = propertiesRelatedTo(apparentSource, target, reportErrors);
51215121
if (result) {
5122-
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportErrors);
5122+
result &= signaturesRelatedTo(apparentSource, target, SignatureKind.Call, reportErrors);
51235123
if (result) {
5124-
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportErrors);
5124+
result &= signaturesRelatedTo(apparentSource, target, SignatureKind.Construct, reportErrors);
51255125
if (result) {
5126-
result &= stringIndexTypesRelatedTo(source, target, reportErrors);
5126+
result &= stringIndexTypesRelatedTo(apparentSource, originalSource, target, reportErrors);
51275127
if (result) {
5128-
result &= numberIndexTypesRelatedTo(source, target, reportErrors);
5128+
result &= numberIndexTypesRelatedTo(apparentSource, originalSource, target, reportErrors);
51295129
}
51305130
}
51315131
}
@@ -5456,12 +5456,17 @@ namespace ts {
54565456
return result;
54575457
}
54585458

5459-
function stringIndexTypesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
5459+
function stringIndexTypesRelatedTo(source: Type, originalSource: Type, target: Type, reportErrors: boolean): Ternary {
54605460
if (relation === identityRelation) {
54615461
return indexTypesIdenticalTo(IndexKind.String, source, target);
54625462
}
54635463
let targetType = getIndexTypeOfType(target, IndexKind.String);
5464-
if (targetType && !(targetType.flags & TypeFlags.Any)) {
5464+
if (targetType) {
5465+
if ((targetType.flags & TypeFlags.Any) && !(originalSource.flags & TypeFlags.Primitive)) {
5466+
// non-primitive assignment to any is always allowed, eg
5467+
// `var x: { [index: string]: any } = { property: 12 };`
5468+
return Ternary.True;
5469+
}
54655470
let sourceType = getIndexTypeOfType(source, IndexKind.String);
54665471
if (!sourceType) {
54675472
if (reportErrors) {
@@ -5481,12 +5486,17 @@ namespace ts {
54815486
return Ternary.True;
54825487
}
54835488

5484-
function numberIndexTypesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
5489+
function numberIndexTypesRelatedTo(source: Type, originalSource: Type, target: Type, reportErrors: boolean): Ternary {
54855490
if (relation === identityRelation) {
54865491
return indexTypesIdenticalTo(IndexKind.Number, source, target);
54875492
}
54885493
let targetType = getIndexTypeOfType(target, IndexKind.Number);
5489-
if (targetType && !(targetType.flags & TypeFlags.Any)) {
5494+
if (targetType) {
5495+
if ((targetType.flags & TypeFlags.Any) && !(originalSource.flags & TypeFlags.Primitive)) {
5496+
// non-primitive assignment to any is always allowed, eg
5497+
// `var x: { [index: number]: any } = { property: 12 };`
5498+
return Ternary.True;
5499+
}
54905500
let sourceStringType = getIndexTypeOfType(source, IndexKind.String);
54915501
let sourceNumberType = getIndexTypeOfType(source, IndexKind.Number);
54925502
if (!(sourceStringType || sourceNumberType)) {

tests/baselines/reference/assignmentCompat1.errors.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ tests/cases/compiler/assignmentCompat1.ts(4,1): error TS2322: Type '{ [index: st
22
Property 'one' is missing in type '{ [index: string]: any; }'.
33
tests/cases/compiler/assignmentCompat1.ts(6,1): error TS2322: Type '{ [index: number]: any; }' is not assignable to type '{ one: number; }'.
44
Property 'one' is missing in type '{ [index: number]: any; }'.
5+
tests/cases/compiler/assignmentCompat1.ts(8,1): error TS2322: Type 'string' is not assignable to type '{ [index: string]: any; }'.
6+
Index signature is missing in type 'String'.
7+
tests/cases/compiler/assignmentCompat1.ts(10,1): error TS2322: Type 'boolean' is not assignable to type '{ [index: number]: any; }'.
8+
Index signature is missing in type 'Boolean'.
59

610

7-
==== tests/cases/compiler/assignmentCompat1.ts (2 errors) ====
11+
==== tests/cases/compiler/assignmentCompat1.ts (4 errors) ====
812
var x = { one: 1 };
913
var y: { [index: string]: any };
1014
var z: { [index: number]: any };
@@ -18,4 +22,14 @@ tests/cases/compiler/assignmentCompat1.ts(6,1): error TS2322: Type '{ [index: nu
1822
!!! error TS2322: Type '{ [index: number]: any; }' is not assignable to type '{ one: number; }'.
1923
!!! error TS2322: Property 'one' is missing in type '{ [index: number]: any; }'.
2024
z = x; // Ok because index signature type is any
25+
y = "foo"; // Error
26+
~
27+
!!! error TS2322: Type 'string' is not assignable to type '{ [index: string]: any; }'.
28+
!!! error TS2322: Index signature is missing in type 'String'.
29+
z = "foo"; // OK, string has numeric indexer
30+
z = false; // Error
31+
~
32+
!!! error TS2322: Type 'boolean' is not assignable to type '{ [index: number]: any; }'.
33+
!!! error TS2322: Index signature is missing in type 'Boolean'.
34+
2135

tests/baselines/reference/assignmentCompat1.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ x = y; // Error
66
y = x; // Ok because index signature type is any
77
x = z; // Error
88
z = x; // Ok because index signature type is any
9+
y = "foo"; // Error
10+
z = "foo"; // OK, string has numeric indexer
11+
z = false; // Error
12+
913

1014

1115
//// [assignmentCompat1.js]
@@ -16,3 +20,6 @@ x = y; // Error
1620
y = x; // Ok because index signature type is any
1721
x = z; // Error
1822
z = x; // Ok because index signature type is any
23+
y = "foo"; // Error
24+
z = "foo"; // OK, string has numeric indexer
25+
z = false; // Error

tests/baselines/reference/indexTypeCheck.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
tests/cases/compiler/indexTypeCheck.ts(2,2): error TS1021: An index signature must have a type annotation.
22
tests/cases/compiler/indexTypeCheck.ts(3,2): error TS1021: An index signature must have a type annotation.
33
tests/cases/compiler/indexTypeCheck.ts(17,2): error TS2413: Numeric index type 'number' is not assignable to string index type 'string'.
4+
tests/cases/compiler/indexTypeCheck.ts(22,2): error TS2413: Numeric index type 'Orange' is not assignable to string index type 'Yellow'.
45
tests/cases/compiler/indexTypeCheck.ts(27,2): error TS2413: Numeric index type 'number' is not assignable to string index type 'string'.
56
tests/cases/compiler/indexTypeCheck.ts(32,3): error TS1096: An index signature must have exactly one parameter.
67
tests/cases/compiler/indexTypeCheck.ts(36,3): error TS1023: An index signature parameter type must be 'string' or 'number'.
78
tests/cases/compiler/indexTypeCheck.ts(51,1): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'.
89

910

10-
==== tests/cases/compiler/indexTypeCheck.ts (7 errors) ====
11+
==== tests/cases/compiler/indexTypeCheck.ts (8 errors) ====
1112
interface Red {
1213
[n:number]; // ok
1314
~~~~~~~~~~~
@@ -36,6 +37,8 @@ tests/cases/compiler/indexTypeCheck.ts(51,1): error TS2342: An index expression
3637

3738
interface Green {
3839
[n:number]: Orange; // error
40+
~~~~~~~~~~~~~~~~~~~
41+
!!! error TS2413: Numeric index type 'Orange' is not assignable to string index type 'Yellow'.
3942
[s:string]: Yellow; // ok
4043
}
4144

tests/baselines/reference/intTypeCheck.errors.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ tests/cases/compiler/intTypeCheck.ts(134,21): error TS1109: Expression expected.
3030
tests/cases/compiler/intTypeCheck.ts(134,22): error TS2304: Cannot find name 'i3'.
3131
tests/cases/compiler/intTypeCheck.ts(135,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
3232
tests/cases/compiler/intTypeCheck.ts(142,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
33+
tests/cases/compiler/intTypeCheck.ts(148,5): error TS2322: Type 'boolean' is not assignable to type 'i4'.
34+
Index signature is missing in type 'Boolean'.
3335
tests/cases/compiler/intTypeCheck.ts(148,21): error TS1109: Expression expected.
3436
tests/cases/compiler/intTypeCheck.ts(148,22): error TS2304: Cannot find name 'i4'.
3537
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.
6668
tests/cases/compiler/intTypeCheck.ts(190,22): error TS2304: Cannot find name 'i7'.
6769
tests/cases/compiler/intTypeCheck.ts(191,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
6870
tests/cases/compiler/intTypeCheck.ts(198,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
71+
tests/cases/compiler/intTypeCheck.ts(204,5): error TS2322: Type 'boolean' is not assignable to type 'i8'.
72+
Index signature is missing in type 'Boolean'.
6973
tests/cases/compiler/intTypeCheck.ts(204,21): error TS1109: Expression expected.
7074
tests/cases/compiler/intTypeCheck.ts(204,22): error TS2304: Cannot find name 'i8'.
7175
tests/cases/compiler/intTypeCheck.ts(205,17): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
7276

7377

74-
==== tests/cases/compiler/intTypeCheck.ts (61 errors) ====
78+
==== tests/cases/compiler/intTypeCheck.ts (63 errors) ====
7579
interface i1 {
7680
//Property Signatures
7781
p;
@@ -280,6 +284,9 @@ tests/cases/compiler/intTypeCheck.ts(205,17): error TS2351: Cannot use 'new' wit
280284
//var obj40: i4 = function foo() { };
281285
var obj41: i4 = <i4> anyVar;
282286
var obj42: i4 = new <i4> anyVar;
287+
~~~~~
288+
!!! error TS2322: Type 'boolean' is not assignable to type 'i4'.
289+
!!! error TS2322: Index signature is missing in type 'Boolean'.
283290
~
284291
!!! error TS1109: Expression expected.
285292
~~
@@ -402,6 +409,9 @@ tests/cases/compiler/intTypeCheck.ts(205,17): error TS2351: Cannot use 'new' wit
402409
//var obj84: i8 = function foo() { };
403410
var obj85: i8 = <i8> anyVar;
404411
var obj86: i8 = new <i8> anyVar;
412+
~~~~~
413+
!!! error TS2322: Type 'boolean' is not assignable to type 'i8'.
414+
!!! error TS2322: Index signature is missing in type 'Boolean'.
405415
~
406416
!!! error TS1109: Expression expected.
407417
~~

tests/cases/compiler/assignmentCompat1.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ x = y; // Error
55
y = x; // Ok because index signature type is any
66
x = z; // Error
77
z = x; // Ok because index signature type is any
8+
y = "foo"; // Error
9+
z = "foo"; // OK, string has numeric indexer
10+
z = false; // Error
11+

0 commit comments

Comments
 (0)