Skip to content

Commit 6e5e09c

Browse files
authored
Reject return type inferences to the autoType or autoArrayType (#27169)
* Reject return type inferences to the autoType or autoArrayType * Accept new error positions
1 parent 0c36266 commit 6e5e09c

6 files changed

+249
-3
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13665,7 +13665,7 @@ namespace ts {
1366513665
// not contain anyFunctionType when we come back to this argument for its second round
1366613666
// of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard
1366713667
// when constructing types from type parameters that had no inference candidates).
13668-
if (source.flags & TypeFlags.ContainsAnyFunctionType || source === silentNeverType) {
13668+
if (source.flags & TypeFlags.ContainsAnyFunctionType || source === silentNeverType || (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType))) {
1366913669
return;
1367013670
}
1367113671
const inference = getInferenceInfoForType(target);
@@ -14904,7 +14904,7 @@ namespace ts {
1490414904
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
1490514905
// on empty arrays are possible without implicit any errors and new element types can be inferred without
1490614906
// type mismatch errors.
14907-
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
14907+
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? autoArrayType : finalizeEvolvingArrayType(evolvedType);
1490814908
if (reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
1490914909
return declaredType;
1491014910
}
@@ -15895,7 +15895,7 @@ namespace ts {
1589515895
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
1589615896
// from declaration to use, and when the variable's declared type doesn't include undefined but the
1589715897
// control flow based type does include undefined.
15898-
if (type === autoType || type === autoArrayType) {
15898+
if (!isEvolvingArrayOperationTarget(node) && (type === autoType || type === autoArrayType)) {
1589915899
if (flowType === autoType || flowType === autoArrayType) {
1590015900
if (noImplicitAny) {
1590115901
error(getNameOfDeclaration(declaration), Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, symbolToString(symbol), typeToString(flowType));
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(7,11): error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
2+
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(9,15): error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
3+
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(15,17): error TS2322: Type 'number' is not assignable to type 'string'.
4+
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(17,19): error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
5+
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(18,22): error TS2322: Type 'number' is not assignable to type 'string'.
6+
7+
8+
==== tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts (5 errors) ====
9+
// repro from https://github.com/Microsoft/TypeScript/issues/25675
10+
// The type of `arg` blocks inference but simplifies to T.
11+
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
12+
console.log(arg.length);
13+
return arg;
14+
}
15+
logLength(42); // error
16+
~~
17+
!!! error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
18+
let z;
19+
z = logLength(42); // no error; T is inferred as `any`
20+
~~
21+
!!! error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
22+
23+
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
24+
console.log(arg[0].length);
25+
return arg;
26+
}
27+
logFirstLength([42]); // error
28+
~~
29+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
30+
let zz = [];
31+
zz.push(logLength(42)); // no error; T is inferred as `any`
32+
~~
33+
!!! error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
34+
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
35+
~~
36+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [inferenceShouldFailOnEvolvingArrays.ts]
2+
// repro from https://github.com/Microsoft/TypeScript/issues/25675
3+
// The type of `arg` blocks inference but simplifies to T.
4+
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
5+
console.log(arg.length);
6+
return arg;
7+
}
8+
logLength(42); // error
9+
let z;
10+
z = logLength(42); // no error; T is inferred as `any`
11+
12+
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
13+
console.log(arg[0].length);
14+
return arg;
15+
}
16+
logFirstLength([42]); // error
17+
let zz = [];
18+
zz.push(logLength(42)); // no error; T is inferred as `any`
19+
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
20+
21+
//// [inferenceShouldFailOnEvolvingArrays.js]
22+
"use strict";
23+
// repro from https://github.com/Microsoft/TypeScript/issues/25675
24+
// The type of `arg` blocks inference but simplifies to T.
25+
function logLength(arg) {
26+
console.log(arg.length);
27+
return arg;
28+
}
29+
logLength(42); // error
30+
var z;
31+
z = logLength(42); // no error; T is inferred as `any`
32+
function logFirstLength(arg) {
33+
console.log(arg[0].length);
34+
return arg;
35+
}
36+
logFirstLength([42]); // error
37+
var zz = [];
38+
zz.push(logLength(42)); // no error; T is inferred as `any`
39+
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
=== tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts ===
2+
// repro from https://github.com/Microsoft/TypeScript/issues/25675
3+
// The type of `arg` blocks inference but simplifies to T.
4+
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
5+
>logLength : Symbol(logLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 0, 0))
6+
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 19))
7+
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 36))
8+
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 55))
9+
>K : Symbol(K, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 63))
10+
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 36))
11+
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 19))
12+
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 36))
13+
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 19))
14+
15+
console.log(arg.length);
16+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
17+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
18+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
19+
>arg.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
20+
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 55))
21+
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
22+
23+
return arg;
24+
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 55))
25+
}
26+
logLength(42); // error
27+
>logLength : Symbol(logLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 0, 0))
28+
29+
let z;
30+
>z : Symbol(z, Decl(inferenceShouldFailOnEvolvingArrays.ts, 7, 3))
31+
32+
z = logLength(42); // no error; T is inferred as `any`
33+
>z : Symbol(z, Decl(inferenceShouldFailOnEvolvingArrays.ts, 7, 3))
34+
>logLength : Symbol(logLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 0, 0))
35+
36+
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
37+
>logFirstLength : Symbol(logFirstLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 8, 18))
38+
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 24))
39+
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 43))
40+
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 62))
41+
>K : Symbol(K, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 70))
42+
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 43))
43+
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 24))
44+
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 43))
45+
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 24))
46+
47+
console.log(arg[0].length);
48+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
49+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
50+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
51+
>arg[0].length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
52+
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 62))
53+
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
54+
55+
return arg;
56+
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 62))
57+
}
58+
logFirstLength([42]); // error
59+
>logFirstLength : Symbol(logFirstLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 8, 18))
60+
61+
let zz = [];
62+
>zz : Symbol(zz, Decl(inferenceShouldFailOnEvolvingArrays.ts, 15, 3))
63+
64+
zz.push(logLength(42)); // no error; T is inferred as `any`
65+
>zz.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
66+
>zz : Symbol(zz, Decl(inferenceShouldFailOnEvolvingArrays.ts, 15, 3))
67+
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
68+
>logLength : Symbol(logLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 0, 0))
69+
70+
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
71+
>zz : Symbol(zz, Decl(inferenceShouldFailOnEvolvingArrays.ts, 15, 3))
72+
>logFirstLength : Symbol(logFirstLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 8, 18))
73+
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
=== tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts ===
2+
// repro from https://github.com/Microsoft/TypeScript/issues/25675
3+
// The type of `arg` blocks inference but simplifies to T.
4+
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
5+
>logLength : <T extends string, U extends string>(arg: { [K in U]: T; }[U]) => T
6+
>arg : { [K in U]: T; }[U]
7+
8+
console.log(arg.length);
9+
>console.log(arg.length) : void
10+
>console.log : (message?: any, ...optionalParams: any[]) => void
11+
>console : Console
12+
>log : (message?: any, ...optionalParams: any[]) => void
13+
>arg.length : number
14+
>arg : { [K in U]: T; }[U]
15+
>length : number
16+
17+
return arg;
18+
>arg : { [K in U]: T; }[U]
19+
}
20+
logLength(42); // error
21+
>logLength(42) : any
22+
>logLength : <T extends string, U extends string>(arg: { [K in U]: T; }[U]) => T
23+
>42 : 42
24+
25+
let z;
26+
>z : any
27+
28+
z = logLength(42); // no error; T is inferred as `any`
29+
>z = logLength(42) : any
30+
>z : any
31+
>logLength(42) : any
32+
>logLength : <T extends string, U extends string>(arg: { [K in U]: T; }[U]) => T
33+
>42 : 42
34+
35+
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
36+
>logFirstLength : <T extends string[], U extends string>(arg: { [K in U]: T; }[U]) => T
37+
>arg : { [K in U]: T; }[U]
38+
39+
console.log(arg[0].length);
40+
>console.log(arg[0].length) : void
41+
>console.log : (message?: any, ...optionalParams: any[]) => void
42+
>console : Console
43+
>log : (message?: any, ...optionalParams: any[]) => void
44+
>arg[0].length : number
45+
>arg[0] : string
46+
>arg : { [K in U]: T; }[U]
47+
>0 : 0
48+
>length : number
49+
50+
return arg;
51+
>arg : { [K in U]: T; }[U]
52+
}
53+
logFirstLength([42]); // error
54+
>logFirstLength([42]) : any
55+
>logFirstLength : <T extends string[], U extends string>(arg: { [K in U]: T; }[U]) => T
56+
>[42] : number[]
57+
>42 : 42
58+
59+
let zz = [];
60+
>zz : any[]
61+
>[] : never[]
62+
63+
zz.push(logLength(42)); // no error; T is inferred as `any`
64+
>zz.push(logLength(42)) : number
65+
>zz.push : (...items: any[]) => number
66+
>zz : any[]
67+
>push : (...items: any[]) => number
68+
>logLength(42) : any
69+
>logLength : <T extends string, U extends string>(arg: { [K in U]: T; }[U]) => T
70+
>42 : 42
71+
72+
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
73+
>zz = logFirstLength([42]) : any
74+
>zz : any[]
75+
>logFirstLength([42]) : any
76+
>logFirstLength : <T extends string[], U extends string>(arg: { [K in U]: T; }[U]) => T
77+
>[42] : number[]
78+
>42 : 42
79+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// @strict: true
2+
// repro from https://github.com/Microsoft/TypeScript/issues/25675
3+
// The type of `arg` blocks inference but simplifies to T.
4+
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
5+
console.log(arg.length);
6+
return arg;
7+
}
8+
logLength(42); // error
9+
let z;
10+
z = logLength(42); // no error; T is inferred as `any`
11+
12+
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
13+
console.log(arg[0].length);
14+
return arg;
15+
}
16+
logFirstLength([42]); // error
17+
let zz = [];
18+
zz.push(logLength(42)); // no error; T is inferred as `any`
19+
zz = logFirstLength([42]); // no error; T is inferred as `any[]`

0 commit comments

Comments
 (0)