Reject return type inferences to the autoType or autoArrayType (#27169)

* Reject return type inferences to the autoType or autoArrayType

* Accept new error positions
This commit is contained in:
Wesley Wigham
2018-10-22 16:44:32 -07:00
committed by GitHub
parent 0c36266706
commit 6e5e09cef8
6 changed files with 249 additions and 3 deletions

View File

@@ -13665,7 +13665,7 @@ namespace ts {
// not contain anyFunctionType when we come back to this argument for its second round
// of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard
// when constructing types from type parameters that had no inference candidates).
if (source.flags & TypeFlags.ContainsAnyFunctionType || source === silentNeverType) {
if (source.flags & TypeFlags.ContainsAnyFunctionType || source === silentNeverType || (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType))) {
return;
}
const inference = getInferenceInfoForType(target);
@@ -14904,7 +14904,7 @@ namespace ts {
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
// on empty arrays are possible without implicit any errors and new element types can be inferred without
// type mismatch errors.
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? autoArrayType : finalizeEvolvingArrayType(evolvedType);
if (reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
return declaredType;
}
@@ -15895,7 +15895,7 @@ namespace ts {
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
// from declaration to use, and when the variable's declared type doesn't include undefined but the
// control flow based type does include undefined.
if (type === autoType || type === autoArrayType) {
if (!isEvolvingArrayOperationTarget(node) && (type === autoType || type === autoArrayType)) {
if (flowType === autoType || flowType === autoArrayType) {
if (noImplicitAny) {
error(getNameOfDeclaration(declaration), Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, symbolToString(symbol), typeToString(flowType));

View File

@@ -0,0 +1,36 @@
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(7,11): error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(9,15): error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(15,17): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(17,19): error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts(18,22): error TS2322: Type 'number' is not assignable to type 'string'.
==== tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts (5 errors) ====
// repro from https://github.com/Microsoft/TypeScript/issues/25675
// The type of `arg` blocks inference but simplifies to T.
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
console.log(arg.length);
return arg;
}
logLength(42); // error
~~
!!! error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
let z;
z = logLength(42); // no error; T is inferred as `any`
~~
!!! error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
console.log(arg[0].length);
return arg;
}
logFirstLength([42]); // error
~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
let zz = [];
zz.push(logLength(42)); // no error; T is inferred as `any`
~~
!!! error TS2345: Argument of type '42' is not assignable to parameter of type 'string'.
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.

View File

@@ -0,0 +1,39 @@
//// [inferenceShouldFailOnEvolvingArrays.ts]
// repro from https://github.com/Microsoft/TypeScript/issues/25675
// The type of `arg` blocks inference but simplifies to T.
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
console.log(arg.length);
return arg;
}
logLength(42); // error
let z;
z = logLength(42); // no error; T is inferred as `any`
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
console.log(arg[0].length);
return arg;
}
logFirstLength([42]); // error
let zz = [];
zz.push(logLength(42)); // no error; T is inferred as `any`
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
//// [inferenceShouldFailOnEvolvingArrays.js]
"use strict";
// repro from https://github.com/Microsoft/TypeScript/issues/25675
// The type of `arg` blocks inference but simplifies to T.
function logLength(arg) {
console.log(arg.length);
return arg;
}
logLength(42); // error
var z;
z = logLength(42); // no error; T is inferred as `any`
function logFirstLength(arg) {
console.log(arg[0].length);
return arg;
}
logFirstLength([42]); // error
var zz = [];
zz.push(logLength(42)); // no error; T is inferred as `any`
zz = logFirstLength([42]); // no error; T is inferred as `any[]`

View File

@@ -0,0 +1,73 @@
=== tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts ===
// repro from https://github.com/Microsoft/TypeScript/issues/25675
// The type of `arg` blocks inference but simplifies to T.
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
>logLength : Symbol(logLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 0, 0))
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 19))
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 36))
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 55))
>K : Symbol(K, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 63))
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 36))
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 19))
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 36))
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 19))
console.log(arg.length);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>arg.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 55))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
return arg;
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 2, 55))
}
logLength(42); // error
>logLength : Symbol(logLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 0, 0))
let z;
>z : Symbol(z, Decl(inferenceShouldFailOnEvolvingArrays.ts, 7, 3))
z = logLength(42); // no error; T is inferred as `any`
>z : Symbol(z, Decl(inferenceShouldFailOnEvolvingArrays.ts, 7, 3))
>logLength : Symbol(logLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 0, 0))
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
>logFirstLength : Symbol(logFirstLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 8, 18))
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 24))
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 43))
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 62))
>K : Symbol(K, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 70))
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 43))
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 24))
>U : Symbol(U, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 43))
>T : Symbol(T, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 24))
console.log(arg[0].length);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>arg[0].length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 62))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
return arg;
>arg : Symbol(arg, Decl(inferenceShouldFailOnEvolvingArrays.ts, 10, 62))
}
logFirstLength([42]); // error
>logFirstLength : Symbol(logFirstLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 8, 18))
let zz = [];
>zz : Symbol(zz, Decl(inferenceShouldFailOnEvolvingArrays.ts, 15, 3))
zz.push(logLength(42)); // no error; T is inferred as `any`
>zz.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>zz : Symbol(zz, Decl(inferenceShouldFailOnEvolvingArrays.ts, 15, 3))
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>logLength : Symbol(logLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 0, 0))
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
>zz : Symbol(zz, Decl(inferenceShouldFailOnEvolvingArrays.ts, 15, 3))
>logFirstLength : Symbol(logFirstLength, Decl(inferenceShouldFailOnEvolvingArrays.ts, 8, 18))

View File

@@ -0,0 +1,79 @@
=== tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts ===
// repro from https://github.com/Microsoft/TypeScript/issues/25675
// The type of `arg` blocks inference but simplifies to T.
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
>logLength : <T extends string, U extends string>(arg: { [K in U]: T; }[U]) => T
>arg : { [K in U]: T; }[U]
console.log(arg.length);
>console.log(arg.length) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>arg.length : number
>arg : { [K in U]: T; }[U]
>length : number
return arg;
>arg : { [K in U]: T; }[U]
}
logLength(42); // error
>logLength(42) : any
>logLength : <T extends string, U extends string>(arg: { [K in U]: T; }[U]) => T
>42 : 42
let z;
>z : any
z = logLength(42); // no error; T is inferred as `any`
>z = logLength(42) : any
>z : any
>logLength(42) : any
>logLength : <T extends string, U extends string>(arg: { [K in U]: T; }[U]) => T
>42 : 42
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
>logFirstLength : <T extends string[], U extends string>(arg: { [K in U]: T; }[U]) => T
>arg : { [K in U]: T; }[U]
console.log(arg[0].length);
>console.log(arg[0].length) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>arg[0].length : number
>arg[0] : string
>arg : { [K in U]: T; }[U]
>0 : 0
>length : number
return arg;
>arg : { [K in U]: T; }[U]
}
logFirstLength([42]); // error
>logFirstLength([42]) : any
>logFirstLength : <T extends string[], U extends string>(arg: { [K in U]: T; }[U]) => T
>[42] : number[]
>42 : 42
let zz = [];
>zz : any[]
>[] : never[]
zz.push(logLength(42)); // no error; T is inferred as `any`
>zz.push(logLength(42)) : number
>zz.push : (...items: any[]) => number
>zz : any[]
>push : (...items: any[]) => number
>logLength(42) : any
>logLength : <T extends string, U extends string>(arg: { [K in U]: T; }[U]) => T
>42 : 42
zz = logFirstLength([42]); // no error; T is inferred as `any[]`
>zz = logFirstLength([42]) : any
>zz : any[]
>logFirstLength([42]) : any
>logFirstLength : <T extends string[], U extends string>(arg: { [K in U]: T; }[U]) => T
>[42] : number[]
>42 : 42

View File

@@ -0,0 +1,19 @@
// @strict: true
// repro from https://github.com/Microsoft/TypeScript/issues/25675
// The type of `arg` blocks inference but simplifies to T.
function logLength<T extends string, U extends string>(arg: { [K in U]: T }[U]): T {
console.log(arg.length);
return arg;
}
logLength(42); // error
let z;
z = logLength(42); // no error; T is inferred as `any`
function logFirstLength<T extends string[], U extends string>(arg: { [K in U]: T }[U]): T {
console.log(arg[0].length);
return arg;
}
logFirstLength([42]); // error
let zz = [];
zz.push(logLength(42)); // no error; T is inferred as `any`
zz = logFirstLength([42]); // no error; T is inferred as `any[]`