diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 082c31416a2..acfcc313490 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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)); diff --git a/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.errors.txt b/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.errors.txt new file mode 100644 index 00000000000..e8081e02cbd --- /dev/null +++ b/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.errors.txt @@ -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(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(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'. \ No newline at end of file diff --git a/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.js b/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.js new file mode 100644 index 00000000000..805634b17e1 --- /dev/null +++ b/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.js @@ -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(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(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[]` diff --git a/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.symbols b/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.symbols new file mode 100644 index 00000000000..287e7f81c86 --- /dev/null +++ b/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.symbols @@ -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(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(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)) + diff --git a/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.types b/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.types new file mode 100644 index 00000000000..2d7876aefe9 --- /dev/null +++ b/tests/baselines/reference/inferenceShouldFailOnEvolvingArrays.types @@ -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(arg: { [K in U]: T }[U]): T { +>logLength : (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 : (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 : (arg: { [K in U]: T; }[U]) => T +>42 : 42 + +function logFirstLength(arg: { [K in U]: T }[U]): T { +>logFirstLength : (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 : (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 : (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 : (arg: { [K in U]: T; }[U]) => T +>[42] : number[] +>42 : 42 + diff --git a/tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts b/tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts new file mode 100644 index 00000000000..13a24ce66f9 --- /dev/null +++ b/tests/cases/compiler/inferenceShouldFailOnEvolvingArrays.ts @@ -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(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(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[]` \ No newline at end of file