From 09271f107d41c91836b196c4e28d1ddd692d8ebc Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 5 Dec 2019 07:09:45 -0800 Subject: [PATCH] Make no inferences from binding patterns with no defaults (#35454) * Use nonInferrableAnyType in types inferred from binding patterns * Add regression tests * Accept new baselines --- src/compiler/checker.ts | 14 +- src/compiler/types.ts | 2 +- .../reference/inferFromBindingPattern.js | 61 ++++++++ .../reference/inferFromBindingPattern.symbols | 130 ++++++++++++++++++ .../reference/inferFromBindingPattern.types | 110 +++++++++++++++ .../conformance/inferFromBindingPattern.ts | 44 ++++++ 6 files changed, 357 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/inferFromBindingPattern.js create mode 100644 tests/baselines/reference/inferFromBindingPattern.symbols create mode 100644 tests/baselines/reference/inferFromBindingPattern.types create mode 100644 tests/cases/conformance/inferFromBindingPattern.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d212077878f..1a5559794be 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -638,6 +638,7 @@ namespace ts { const autoType = createIntrinsicType(TypeFlags.Any, "any"); const wildcardType = createIntrinsicType(TypeFlags.Any, "any"); const errorType = createIntrinsicType(TypeFlags.Any, "error"); + const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType); const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType); @@ -7156,7 +7157,11 @@ namespace ts { if (reportErrors && !declarationBelongsToPrivateAmbientMember(element)) { reportImplicitAny(element, anyType); } - return anyType; + // When we're including the pattern in the type (an indication we're obtaining a contextual type), we + // use the non-inferrable any type. Inference will never directly infer this type, but it is possible + // to infer a type that contains it, e.g. for a binding pattern like [foo] or { foo }. In such cases, + // widening of the binding pattern type substitutes a regular any for the non-inferrable any. + return includePatternInType ? nonInferrableAnyType : anyType; } // Return the type implied by an object binding pattern @@ -7188,6 +7193,7 @@ namespace ts { result.objectFlags |= objectFlags; if (includePatternInType) { result.pattern = pattern; + result.objectFlags |= ObjectFlags.ContainsObjectOrArrayLiteral; } return result; } @@ -7206,6 +7212,7 @@ namespace ts { if (includePatternInType) { result = cloneTypeReference(result); result.pattern = pattern; + result.objectFlags |= ObjectFlags.ContainsObjectOrArrayLiteral; } return result; } @@ -17025,7 +17032,7 @@ namespace ts { return type.widened; } let result: Type | undefined; - if (type.flags & TypeFlags.Nullable) { + if (type.flags & (TypeFlags.Any | TypeFlags.Nullable)) { result = anyType; } else if (isObjectLiteralType(type)) { @@ -17535,7 +17542,8 @@ 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 (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === silentNeverType || (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType))) { + if (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === nonInferrableAnyType || source === silentNeverType || + (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType))) { return; } const inference = getInferenceInfoForType(target); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f5dd1361d37..76f98537f9b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4232,7 +4232,7 @@ namespace ts { Instantiable = InstantiableNonPrimitive | InstantiablePrimitive, StructuredOrInstantiable = StructuredType | Instantiable, /* @internal */ - ObjectFlagsType = Nullable | Never | Object | Union | Intersection, + ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection, /* @internal */ Simplifiable = IndexedAccess | Conditional, // 'Narrowable' types are types where narrowing actually narrows. diff --git a/tests/baselines/reference/inferFromBindingPattern.js b/tests/baselines/reference/inferFromBindingPattern.js new file mode 100644 index 00000000000..9bf48577c99 --- /dev/null +++ b/tests/baselines/reference/inferFromBindingPattern.js @@ -0,0 +1,61 @@ +//// [inferFromBindingPattern.ts] +declare function f1(): T; +declare function f2(): [T]; +declare function f3(): { x: T }; + +let x1 = f1(); // string +let [x2] = f2(); // string +let { x: x3 } = f3(); // string + +// Repro from #30379 + +function foo(): [T] { + return [42 as any] +} +const [x] = foo(); // [number] + +// Repro from #35291 + +interface SelectProps { + selector?: (obj: T) => K; +} + +type SelectResult = [K, T]; + +interface Person { + name: string; + surname: string; +} + +declare function selectJohn(props?: SelectProps): SelectResult; + +const [person] = selectJohn(); +const [any, whatever] = selectJohn(); +const john = selectJohn(); +const [personAgain, nufinspecial] = john; + +// Repro from #35291 + +declare function makeTuple(arg: T1): [T1]; +declare function stringy(arg?: T): T; + +const isStringTuple = makeTuple(stringy()); // [string] +const [isAny] = makeTuple(stringy()); // [string] + + +//// [inferFromBindingPattern.js] +"use strict"; +var x1 = f1(); // string +var x2 = f2()[0]; // string +var x3 = f3().x; // string +// Repro from #30379 +function foo() { + return [42]; +} +var x = foo()[0]; // [number] +var person = selectJohn()[0]; +var _a = selectJohn(), any = _a[0], whatever = _a[1]; +var john = selectJohn(); +var personAgain = john[0], nufinspecial = john[1]; +var isStringTuple = makeTuple(stringy()); // [string] +var isAny = makeTuple(stringy())[0]; // [string] diff --git a/tests/baselines/reference/inferFromBindingPattern.symbols b/tests/baselines/reference/inferFromBindingPattern.symbols new file mode 100644 index 00000000000..df6b0e8c4bf --- /dev/null +++ b/tests/baselines/reference/inferFromBindingPattern.symbols @@ -0,0 +1,130 @@ +=== tests/cases/conformance/inferFromBindingPattern.ts === +declare function f1(): T; +>f1 : Symbol(f1, Decl(inferFromBindingPattern.ts, 0, 0)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 0, 20)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 0, 20)) + +declare function f2(): [T]; +>f2 : Symbol(f2, Decl(inferFromBindingPattern.ts, 0, 43)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 1, 20)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 1, 20)) + +declare function f3(): { x: T }; +>f3 : Symbol(f3, Decl(inferFromBindingPattern.ts, 1, 45)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 2, 20)) +>x : Symbol(x, Decl(inferFromBindingPattern.ts, 2, 42)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 2, 20)) + +let x1 = f1(); // string +>x1 : Symbol(x1, Decl(inferFromBindingPattern.ts, 4, 3)) +>f1 : Symbol(f1, Decl(inferFromBindingPattern.ts, 0, 0)) + +let [x2] = f2(); // string +>x2 : Symbol(x2, Decl(inferFromBindingPattern.ts, 5, 5)) +>f2 : Symbol(f2, Decl(inferFromBindingPattern.ts, 0, 43)) + +let { x: x3 } = f3(); // string +>x : Symbol(x, Decl(inferFromBindingPattern.ts, 2, 42)) +>x3 : Symbol(x3, Decl(inferFromBindingPattern.ts, 6, 5)) +>f3 : Symbol(f3, Decl(inferFromBindingPattern.ts, 1, 45)) + +// Repro from #30379 + +function foo(): [T] { +>foo : Symbol(foo, Decl(inferFromBindingPattern.ts, 6, 21)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 10, 13)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 10, 13)) + + return [42 as any] +} +const [x] = foo(); // [number] +>x : Symbol(x, Decl(inferFromBindingPattern.ts, 13, 7)) +>foo : Symbol(foo, Decl(inferFromBindingPattern.ts, 6, 21)) + +// Repro from #35291 + +interface SelectProps { +>SelectProps : Symbol(SelectProps, Decl(inferFromBindingPattern.ts, 13, 18)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 17, 22)) +>K : Symbol(K, Decl(inferFromBindingPattern.ts, 17, 24)) + + selector?: (obj: T) => K; +>selector : Symbol(SelectProps.selector, Decl(inferFromBindingPattern.ts, 17, 29)) +>obj : Symbol(obj, Decl(inferFromBindingPattern.ts, 18, 14)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 17, 22)) +>K : Symbol(K, Decl(inferFromBindingPattern.ts, 17, 24)) +} + +type SelectResult = [K, T]; +>SelectResult : Symbol(SelectResult, Decl(inferFromBindingPattern.ts, 19, 1)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 21, 18)) +>K : Symbol(K, Decl(inferFromBindingPattern.ts, 21, 20)) +>K : Symbol(K, Decl(inferFromBindingPattern.ts, 21, 20)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 21, 18)) + +interface Person { +>Person : Symbol(Person, Decl(inferFromBindingPattern.ts, 21, 33)) + + name: string; +>name : Symbol(Person.name, Decl(inferFromBindingPattern.ts, 23, 18)) + + surname: string; +>surname : Symbol(Person.surname, Decl(inferFromBindingPattern.ts, 24, 15)) +} + +declare function selectJohn(props?: SelectProps): SelectResult; +>selectJohn : Symbol(selectJohn, Decl(inferFromBindingPattern.ts, 26, 1)) +>K : Symbol(K, Decl(inferFromBindingPattern.ts, 28, 28)) +>Person : Symbol(Person, Decl(inferFromBindingPattern.ts, 21, 33)) +>props : Symbol(props, Decl(inferFromBindingPattern.ts, 28, 40)) +>SelectProps : Symbol(SelectProps, Decl(inferFromBindingPattern.ts, 13, 18)) +>Person : Symbol(Person, Decl(inferFromBindingPattern.ts, 21, 33)) +>K : Symbol(K, Decl(inferFromBindingPattern.ts, 28, 28)) +>SelectResult : Symbol(SelectResult, Decl(inferFromBindingPattern.ts, 19, 1)) +>Person : Symbol(Person, Decl(inferFromBindingPattern.ts, 21, 33)) +>K : Symbol(K, Decl(inferFromBindingPattern.ts, 28, 28)) + +const [person] = selectJohn(); +>person : Symbol(person, Decl(inferFromBindingPattern.ts, 30, 7)) +>selectJohn : Symbol(selectJohn, Decl(inferFromBindingPattern.ts, 26, 1)) + +const [any, whatever] = selectJohn(); +>any : Symbol(any, Decl(inferFromBindingPattern.ts, 31, 7)) +>whatever : Symbol(whatever, Decl(inferFromBindingPattern.ts, 31, 11)) +>selectJohn : Symbol(selectJohn, Decl(inferFromBindingPattern.ts, 26, 1)) + +const john = selectJohn(); +>john : Symbol(john, Decl(inferFromBindingPattern.ts, 32, 5)) +>selectJohn : Symbol(selectJohn, Decl(inferFromBindingPattern.ts, 26, 1)) + +const [personAgain, nufinspecial] = john; +>personAgain : Symbol(personAgain, Decl(inferFromBindingPattern.ts, 33, 7)) +>nufinspecial : Symbol(nufinspecial, Decl(inferFromBindingPattern.ts, 33, 19)) +>john : Symbol(john, Decl(inferFromBindingPattern.ts, 32, 5)) + +// Repro from #35291 + +declare function makeTuple(arg: T1): [T1]; +>makeTuple : Symbol(makeTuple, Decl(inferFromBindingPattern.ts, 33, 41)) +>T1 : Symbol(T1, Decl(inferFromBindingPattern.ts, 37, 27)) +>arg : Symbol(arg, Decl(inferFromBindingPattern.ts, 37, 31)) +>T1 : Symbol(T1, Decl(inferFromBindingPattern.ts, 37, 27)) +>T1 : Symbol(T1, Decl(inferFromBindingPattern.ts, 37, 27)) + +declare function stringy(arg?: T): T; +>stringy : Symbol(stringy, Decl(inferFromBindingPattern.ts, 37, 46)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 38, 25)) +>arg : Symbol(arg, Decl(inferFromBindingPattern.ts, 38, 37)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 38, 25)) +>T : Symbol(T, Decl(inferFromBindingPattern.ts, 38, 25)) + +const isStringTuple = makeTuple(stringy()); // [string] +>isStringTuple : Symbol(isStringTuple, Decl(inferFromBindingPattern.ts, 40, 5)) +>makeTuple : Symbol(makeTuple, Decl(inferFromBindingPattern.ts, 33, 41)) +>stringy : Symbol(stringy, Decl(inferFromBindingPattern.ts, 37, 46)) + +const [isAny] = makeTuple(stringy()); // [string] +>isAny : Symbol(isAny, Decl(inferFromBindingPattern.ts, 41, 7)) +>makeTuple : Symbol(makeTuple, Decl(inferFromBindingPattern.ts, 33, 41)) +>stringy : Symbol(stringy, Decl(inferFromBindingPattern.ts, 37, 46)) + diff --git a/tests/baselines/reference/inferFromBindingPattern.types b/tests/baselines/reference/inferFromBindingPattern.types new file mode 100644 index 00000000000..cd8ee76faf8 --- /dev/null +++ b/tests/baselines/reference/inferFromBindingPattern.types @@ -0,0 +1,110 @@ +=== tests/cases/conformance/inferFromBindingPattern.ts === +declare function f1(): T; +>f1 : () => T + +declare function f2(): [T]; +>f2 : () => [T] + +declare function f3(): { x: T }; +>f3 : () => { x: T; } +>x : T + +let x1 = f1(); // string +>x1 : string +>f1() : string +>f1 : () => T + +let [x2] = f2(); // string +>x2 : string +>f2() : [string] +>f2 : () => [T] + +let { x: x3 } = f3(); // string +>x : any +>x3 : string +>f3() : { x: string; } +>f3 : () => { x: T; } + +// Repro from #30379 + +function foo(): [T] { +>foo : () => [T] + + return [42 as any] +>[42 as any] : [any] +>42 as any : any +>42 : 42 +} +const [x] = foo(); // [number] +>x : number +>foo() : [number] +>foo : () => [T] + +// Repro from #35291 + +interface SelectProps { + selector?: (obj: T) => K; +>selector : ((obj: T) => K) | undefined +>obj : T +} + +type SelectResult = [K, T]; +>SelectResult : SelectResult + +interface Person { + name: string; +>name : string + + surname: string; +>surname : string +} + +declare function selectJohn(props?: SelectProps): SelectResult; +>selectJohn : (props?: SelectProps | undefined) => SelectResult +>props : SelectProps | undefined + +const [person] = selectJohn(); +>person : Person +>selectJohn() : SelectResult +>selectJohn : (props?: SelectProps | undefined) => SelectResult + +const [any, whatever] = selectJohn(); +>any : Person +>whatever : Person +>selectJohn() : SelectResult +>selectJohn : (props?: SelectProps | undefined) => SelectResult + +const john = selectJohn(); +>john : SelectResult +>selectJohn() : SelectResult +>selectJohn : (props?: SelectProps | undefined) => SelectResult + +const [personAgain, nufinspecial] = john; +>personAgain : Person +>nufinspecial : Person +>john : SelectResult + +// Repro from #35291 + +declare function makeTuple(arg: T1): [T1]; +>makeTuple : (arg: T1) => [T1] +>arg : T1 + +declare function stringy(arg?: T): T; +>stringy : (arg?: T | undefined) => T +>arg : T | undefined + +const isStringTuple = makeTuple(stringy()); // [string] +>isStringTuple : [string] +>makeTuple(stringy()) : [string] +>makeTuple : (arg: T1) => [T1] +>stringy() : string +>stringy : (arg?: T | undefined) => T + +const [isAny] = makeTuple(stringy()); // [string] +>isAny : string +>makeTuple(stringy()) : [string] +>makeTuple : (arg: T1) => [T1] +>stringy() : string +>stringy : (arg?: T | undefined) => T + diff --git a/tests/cases/conformance/inferFromBindingPattern.ts b/tests/cases/conformance/inferFromBindingPattern.ts new file mode 100644 index 00000000000..c34010b652e --- /dev/null +++ b/tests/cases/conformance/inferFromBindingPattern.ts @@ -0,0 +1,44 @@ +// @strict: true + +declare function f1(): T; +declare function f2(): [T]; +declare function f3(): { x: T }; + +let x1 = f1(); // string +let [x2] = f2(); // string +let { x: x3 } = f3(); // string + +// Repro from #30379 + +function foo(): [T] { + return [42 as any] +} +const [x] = foo(); // [number] + +// Repro from #35291 + +interface SelectProps { + selector?: (obj: T) => K; +} + +type SelectResult = [K, T]; + +interface Person { + name: string; + surname: string; +} + +declare function selectJohn(props?: SelectProps): SelectResult; + +const [person] = selectJohn(); +const [any, whatever] = selectJohn(); +const john = selectJohn(); +const [personAgain, nufinspecial] = john; + +// Repro from #35291 + +declare function makeTuple(arg: T1): [T1]; +declare function stringy(arg?: T): T; + +const isStringTuple = makeTuple(stringy()); // [string] +const [isAny] = makeTuple(stringy()); // [string]