Skip to content

Commit 0a86b23

Browse files
committed
Add implicit any errors for destructuring computed names which arent late bound and have no corresponding index
1 parent 391c056 commit 0a86b23

6 files changed

+223
-1
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4173,7 +4173,23 @@ namespace ts {
41734173
const isLate = isLateBindableName(name);
41744174
const isWellKnown = isComputedPropertyName(name) && isWellKnownSymbolSyntactically(name.expression);
41754175
if (!isLate && !isWellKnown && isComputedNonLiteralName(name)) {
4176-
return anyType;
4176+
const exprType = checkExpression((name as ComputedPropertyName).expression);
4177+
let indexerType: Type;
4178+
if (isTypeAssignableToKind(exprType, TypeFlags.NumberLike)) {
4179+
indexerType = getIndexTypeOfType(parentType, IndexKind.Number);
4180+
}
4181+
else if (isTypeAssignableToKind(exprType, TypeFlags.StringLike)) {
4182+
indexerType = getIndexTypeOfType(parentType, IndexKind.String);
4183+
}
4184+
if (!indexerType && noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) {
4185+
if (getIndexTypeOfType(parentType, IndexKind.Number)) {
4186+
error(declaration, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number);
4187+
}
4188+
else {
4189+
error(declaration, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(parentType));
4190+
}
4191+
}
4192+
return indexerType || anyType;
41774193
}
41784194

41794195
// Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(2,15): error TS7017: Element implicitly has an 'any' type because type '{ prop: string; }' has no index signature.
2+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(10,15): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
3+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(16,15): error TS7017: Element implicitly has an 'any' type because type '{ [idx: string]: string; }' has no index signature.
4+
5+
6+
==== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts (3 errors) ====
7+
let named = "foo";
8+
let {[named]: prop} = {prop: "foo"};
9+
~~~~
10+
!!! error TS7017: Element implicitly has an 'any' type because type '{ prop: string; }' has no index signature.
11+
void prop;
12+
13+
const numIndexed: {[idx: number]: string} = null as any;
14+
const strIndexed: {[idx: string]: string} = null as any;
15+
16+
let numed = 6;
17+
18+
let {[named]: prop2} = numIndexed;
19+
~~~~~
20+
!!! error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
21+
void prop2;
22+
let {[numed]: prop3} = numIndexed;
23+
void prop3;
24+
let {[named]: prop4} = strIndexed;
25+
void prop4;
26+
let {[numed]: prop5} = strIndexed;
27+
~~~~~
28+
!!! error TS7017: Element implicitly has an 'any' type because type '{ [idx: string]: string; }' has no index signature.
29+
void prop5;
30+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//// [lateBoundDestructuringImplicitAnyError.ts]
2+
let named = "foo";
3+
let {[named]: prop} = {prop: "foo"};
4+
void prop;
5+
6+
const numIndexed: {[idx: number]: string} = null as any;
7+
const strIndexed: {[idx: string]: string} = null as any;
8+
9+
let numed = 6;
10+
11+
let {[named]: prop2} = numIndexed;
12+
void prop2;
13+
let {[numed]: prop3} = numIndexed;
14+
void prop3;
15+
let {[named]: prop4} = strIndexed;
16+
void prop4;
17+
let {[numed]: prop5} = strIndexed;
18+
void prop5;
19+
20+
21+
//// [lateBoundDestructuringImplicitAnyError.js]
22+
var named = "foo";
23+
var _a = named, prop = { prop: "foo" }[_a];
24+
void prop;
25+
var numIndexed = null;
26+
var strIndexed = null;
27+
var numed = 6;
28+
var _b = named, prop2 = numIndexed[_b];
29+
void prop2;
30+
var _c = numed, prop3 = numIndexed[_c];
31+
void prop3;
32+
var _d = named, prop4 = strIndexed[_d];
33+
void prop4;
34+
var _e = numed, prop5 = strIndexed[_e];
35+
void prop5;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
=== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts ===
2+
let named = "foo";
3+
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
4+
5+
let {[named]: prop} = {prop: "foo"};
6+
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
7+
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 5))
8+
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 23))
9+
10+
void prop;
11+
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 5))
12+
13+
const numIndexed: {[idx: number]: string} = null as any;
14+
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
15+
>idx : Symbol(idx, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 20))
16+
17+
const strIndexed: {[idx: string]: string} = null as any;
18+
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
19+
>idx : Symbol(idx, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 20))
20+
21+
let numed = 6;
22+
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))
23+
24+
let {[named]: prop2} = numIndexed;
25+
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
26+
>prop2 : Symbol(prop2, Decl(lateBoundDestructuringImplicitAnyError.ts, 9, 5))
27+
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
28+
29+
void prop2;
30+
>prop2 : Symbol(prop2, Decl(lateBoundDestructuringImplicitAnyError.ts, 9, 5))
31+
32+
let {[numed]: prop3} = numIndexed;
33+
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))
34+
>prop3 : Symbol(prop3, Decl(lateBoundDestructuringImplicitAnyError.ts, 11, 5))
35+
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
36+
37+
void prop3;
38+
>prop3 : Symbol(prop3, Decl(lateBoundDestructuringImplicitAnyError.ts, 11, 5))
39+
40+
let {[named]: prop4} = strIndexed;
41+
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
42+
>prop4 : Symbol(prop4, Decl(lateBoundDestructuringImplicitAnyError.ts, 13, 5))
43+
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
44+
45+
void prop4;
46+
>prop4 : Symbol(prop4, Decl(lateBoundDestructuringImplicitAnyError.ts, 13, 5))
47+
48+
let {[numed]: prop5} = strIndexed;
49+
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))
50+
>prop5 : Symbol(prop5, Decl(lateBoundDestructuringImplicitAnyError.ts, 15, 5))
51+
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
52+
53+
void prop5;
54+
>prop5 : Symbol(prop5, Decl(lateBoundDestructuringImplicitAnyError.ts, 15, 5))
55+
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
=== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts ===
2+
let named = "foo";
3+
>named : string
4+
>"foo" : "foo"
5+
6+
let {[named]: prop} = {prop: "foo"};
7+
>named : string
8+
>prop : any
9+
>{prop: "foo"} : { prop: string; }
10+
>prop : string
11+
>"foo" : "foo"
12+
13+
void prop;
14+
>void prop : undefined
15+
>prop : any
16+
17+
const numIndexed: {[idx: number]: string} = null as any;
18+
>numIndexed : { [idx: number]: string; }
19+
>idx : number
20+
>null as any : any
21+
>null : null
22+
23+
const strIndexed: {[idx: string]: string} = null as any;
24+
>strIndexed : { [idx: string]: string; }
25+
>idx : string
26+
>null as any : any
27+
>null : null
28+
29+
let numed = 6;
30+
>numed : number
31+
>6 : 6
32+
33+
let {[named]: prop2} = numIndexed;
34+
>named : string
35+
>prop2 : any
36+
>numIndexed : { [idx: number]: string; }
37+
38+
void prop2;
39+
>void prop2 : undefined
40+
>prop2 : any
41+
42+
let {[numed]: prop3} = numIndexed;
43+
>numed : number
44+
>prop3 : string
45+
>numIndexed : { [idx: number]: string; }
46+
47+
void prop3;
48+
>void prop3 : undefined
49+
>prop3 : string
50+
51+
let {[named]: prop4} = strIndexed;
52+
>named : string
53+
>prop4 : string
54+
>strIndexed : { [idx: string]: string; }
55+
56+
void prop4;
57+
>void prop4 : undefined
58+
>prop4 : string
59+
60+
let {[numed]: prop5} = strIndexed;
61+
>numed : number
62+
>prop5 : any
63+
>strIndexed : { [idx: string]: string; }
64+
65+
void prop5;
66+
>void prop5 : undefined
67+
>prop5 : any
68+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @noImplicitAny: true
2+
let named = "foo";
3+
let {[named]: prop} = {prop: "foo"};
4+
void prop;
5+
6+
const numIndexed: {[idx: number]: string} = null as any;
7+
const strIndexed: {[idx: string]: string} = null as any;
8+
9+
let numed = 6;
10+
11+
let {[named]: prop2} = numIndexed;
12+
void prop2;
13+
let {[numed]: prop3} = numIndexed;
14+
void prop3;
15+
let {[named]: prop4} = strIndexed;
16+
void prop4;
17+
let {[numed]: prop5} = strIndexed;
18+
void prop5;

0 commit comments

Comments
 (0)