diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f30f8e9d00a42..ac599ae58cb99 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -791,6 +791,7 @@ namespace ts { const wildcardType = createIntrinsicType(TypeFlags.Any, "any"); const errorType = createIntrinsicType(TypeFlags.Any, "error"); const unresolvedType = createIntrinsicType(TypeFlags.Any, "unresolved"); + const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType); const intrinsicMarkerType = createIntrinsicType(TypeFlags.Any, "intrinsic"); const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); const nonNullUnknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); @@ -9560,7 +9561,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 a 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 @@ -22657,7 +22662,10 @@ namespace ts { // // This flag is infectious; if we produce Box (where never is silentNeverType), Box is // also non-inferrable. - if (getObjectFlags(source) & ObjectFlags.NonInferrableType) { + // + // As a special case, also ignore nonInferrableAnyType, which is a special form of the any type + // used as a stand-in for binding elements when they are being inferred. + if (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === nonInferrableAnyType) { return; } if (!inference.isFixed) { diff --git a/tests/baselines/reference/inferStringLiteralUnionForBindingElement.js b/tests/baselines/reference/inferStringLiteralUnionForBindingElement.js new file mode 100644 index 0000000000000..e5c5527287a16 --- /dev/null +++ b/tests/baselines/reference/inferStringLiteralUnionForBindingElement.js @@ -0,0 +1,41 @@ +//// [inferStringLiteralUnionForBindingElement.ts] +declare function func(arg: { keys: T[] }): { readonly keys: T[]; readonly firstKey: T; }; + +function func1() { + const { firstKey } = func({keys: ["aa", "bb"]}) + const a: "aa" | "bb" = firstKey; + + const { keys } = func({keys: ["aa", "bb"]}) + const b: ("aa" | "bb")[] = keys; +} + +function func2() { + const { keys, firstKey } = func({keys: ["aa", "bb"]}) + const a: "aa" | "bb" = firstKey; + const b: ("aa" | "bb")[] = keys; +} + +function func3() { + const x = func({keys: ["aa", "bb"]}) + const a: "aa" | "bb" = x.firstKey; + const b: ("aa" | "bb")[] = x.keys; +} + + +//// [inferStringLiteralUnionForBindingElement.js] +function func1() { + var firstKey = func({ keys: ["aa", "bb"] }).firstKey; + var a = firstKey; + var keys = func({ keys: ["aa", "bb"] }).keys; + var b = keys; +} +function func2() { + var _a = func({ keys: ["aa", "bb"] }), keys = _a.keys, firstKey = _a.firstKey; + var a = firstKey; + var b = keys; +} +function func3() { + var x = func({ keys: ["aa", "bb"] }); + var a = x.firstKey; + var b = x.keys; +} diff --git a/tests/baselines/reference/inferStringLiteralUnionForBindingElement.symbols b/tests/baselines/reference/inferStringLiteralUnionForBindingElement.symbols new file mode 100644 index 0000000000000..1b19c0132ce79 --- /dev/null +++ b/tests/baselines/reference/inferStringLiteralUnionForBindingElement.symbols @@ -0,0 +1,73 @@ +=== tests/cases/compiler/inferStringLiteralUnionForBindingElement.ts === +declare function func(arg: { keys: T[] }): { readonly keys: T[]; readonly firstKey: T; }; +>func : Symbol(func, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 0)) +>T : Symbol(T, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 22)) +>arg : Symbol(arg, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 40)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 46)) +>T : Symbol(T, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 22)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 62)) +>T : Symbol(T, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 22)) +>firstKey : Symbol(firstKey, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 82)) +>T : Symbol(T, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 22)) + +function func1() { +>func1 : Symbol(func1, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 107)) + + const { firstKey } = func({keys: ["aa", "bb"]}) +>firstKey : Symbol(firstKey, Decl(inferStringLiteralUnionForBindingElement.ts, 3, 11)) +>func : Symbol(func, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 0)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 3, 31)) + + const a: "aa" | "bb" = firstKey; +>a : Symbol(a, Decl(inferStringLiteralUnionForBindingElement.ts, 4, 9)) +>firstKey : Symbol(firstKey, Decl(inferStringLiteralUnionForBindingElement.ts, 3, 11)) + + const { keys } = func({keys: ["aa", "bb"]}) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 6, 11)) +>func : Symbol(func, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 0)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 6, 27)) + + const b: ("aa" | "bb")[] = keys; +>b : Symbol(b, Decl(inferStringLiteralUnionForBindingElement.ts, 7, 9)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 6, 11)) +} + +function func2() { +>func2 : Symbol(func2, Decl(inferStringLiteralUnionForBindingElement.ts, 8, 1)) + + const { keys, firstKey } = func({keys: ["aa", "bb"]}) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 11, 11)) +>firstKey : Symbol(firstKey, Decl(inferStringLiteralUnionForBindingElement.ts, 11, 17)) +>func : Symbol(func, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 0)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 11, 37)) + + const a: "aa" | "bb" = firstKey; +>a : Symbol(a, Decl(inferStringLiteralUnionForBindingElement.ts, 12, 9)) +>firstKey : Symbol(firstKey, Decl(inferStringLiteralUnionForBindingElement.ts, 11, 17)) + + const b: ("aa" | "bb")[] = keys; +>b : Symbol(b, Decl(inferStringLiteralUnionForBindingElement.ts, 13, 9)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 11, 11)) +} + +function func3() { +>func3 : Symbol(func3, Decl(inferStringLiteralUnionForBindingElement.ts, 14, 1)) + + const x = func({keys: ["aa", "bb"]}) +>x : Symbol(x, Decl(inferStringLiteralUnionForBindingElement.ts, 17, 9)) +>func : Symbol(func, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 0)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 17, 20)) + + const a: "aa" | "bb" = x.firstKey; +>a : Symbol(a, Decl(inferStringLiteralUnionForBindingElement.ts, 18, 9)) +>x.firstKey : Symbol(firstKey, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 82)) +>x : Symbol(x, Decl(inferStringLiteralUnionForBindingElement.ts, 17, 9)) +>firstKey : Symbol(firstKey, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 82)) + + const b: ("aa" | "bb")[] = x.keys; +>b : Symbol(b, Decl(inferStringLiteralUnionForBindingElement.ts, 19, 9)) +>x.keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 62)) +>x : Symbol(x, Decl(inferStringLiteralUnionForBindingElement.ts, 17, 9)) +>keys : Symbol(keys, Decl(inferStringLiteralUnionForBindingElement.ts, 0, 62)) +} + diff --git a/tests/baselines/reference/inferStringLiteralUnionForBindingElement.types b/tests/baselines/reference/inferStringLiteralUnionForBindingElement.types new file mode 100644 index 0000000000000..0d3cf898877f1 --- /dev/null +++ b/tests/baselines/reference/inferStringLiteralUnionForBindingElement.types @@ -0,0 +1,89 @@ +=== tests/cases/compiler/inferStringLiteralUnionForBindingElement.ts === +declare function func(arg: { keys: T[] }): { readonly keys: T[]; readonly firstKey: T; }; +>func : (arg: { keys: T[];}) => { readonly keys: T[]; readonly firstKey: T;} +>arg : { keys: T[]; } +>keys : T[] +>keys : T[] +>firstKey : T + +function func1() { +>func1 : () => void + + const { firstKey } = func({keys: ["aa", "bb"]}) +>firstKey : "aa" | "bb" +>func({keys: ["aa", "bb"]}) : { readonly keys: ("aa" | "bb")[]; readonly firstKey: "aa" | "bb"; } +>func : (arg: { keys: T[]; }) => { readonly keys: T[]; readonly firstKey: T; } +>{keys: ["aa", "bb"]} : { keys: ("aa" | "bb")[]; } +>keys : ("aa" | "bb")[] +>["aa", "bb"] : ("aa" | "bb")[] +>"aa" : "aa" +>"bb" : "bb" + + const a: "aa" | "bb" = firstKey; +>a : "aa" | "bb" +>firstKey : "aa" | "bb" + + const { keys } = func({keys: ["aa", "bb"]}) +>keys : ("aa" | "bb")[] +>func({keys: ["aa", "bb"]}) : { readonly keys: ("aa" | "bb")[]; readonly firstKey: "aa" | "bb"; } +>func : (arg: { keys: T[]; }) => { readonly keys: T[]; readonly firstKey: T; } +>{keys: ["aa", "bb"]} : { keys: ("aa" | "bb")[]; } +>keys : ("aa" | "bb")[] +>["aa", "bb"] : ("aa" | "bb")[] +>"aa" : "aa" +>"bb" : "bb" + + const b: ("aa" | "bb")[] = keys; +>b : ("aa" | "bb")[] +>keys : ("aa" | "bb")[] +} + +function func2() { +>func2 : () => void + + const { keys, firstKey } = func({keys: ["aa", "bb"]}) +>keys : ("aa" | "bb")[] +>firstKey : "aa" | "bb" +>func({keys: ["aa", "bb"]}) : { readonly keys: ("aa" | "bb")[]; readonly firstKey: "aa" | "bb"; } +>func : (arg: { keys: T[]; }) => { readonly keys: T[]; readonly firstKey: T; } +>{keys: ["aa", "bb"]} : { keys: ("aa" | "bb")[]; } +>keys : ("aa" | "bb")[] +>["aa", "bb"] : ("aa" | "bb")[] +>"aa" : "aa" +>"bb" : "bb" + + const a: "aa" | "bb" = firstKey; +>a : "aa" | "bb" +>firstKey : "aa" | "bb" + + const b: ("aa" | "bb")[] = keys; +>b : ("aa" | "bb")[] +>keys : ("aa" | "bb")[] +} + +function func3() { +>func3 : () => void + + const x = func({keys: ["aa", "bb"]}) +>x : { readonly keys: ("aa" | "bb")[]; readonly firstKey: "aa" | "bb"; } +>func({keys: ["aa", "bb"]}) : { readonly keys: ("aa" | "bb")[]; readonly firstKey: "aa" | "bb"; } +>func : (arg: { keys: T[]; }) => { readonly keys: T[]; readonly firstKey: T; } +>{keys: ["aa", "bb"]} : { keys: ("aa" | "bb")[]; } +>keys : ("aa" | "bb")[] +>["aa", "bb"] : ("aa" | "bb")[] +>"aa" : "aa" +>"bb" : "bb" + + const a: "aa" | "bb" = x.firstKey; +>a : "aa" | "bb" +>x.firstKey : "aa" | "bb" +>x : { readonly keys: ("aa" | "bb")[]; readonly firstKey: "aa" | "bb"; } +>firstKey : "aa" | "bb" + + const b: ("aa" | "bb")[] = x.keys; +>b : ("aa" | "bb")[] +>x.keys : ("aa" | "bb")[] +>x : { readonly keys: ("aa" | "bb")[]; readonly firstKey: "aa" | "bb"; } +>keys : ("aa" | "bb")[] +} + diff --git a/tests/cases/compiler/inferStringLiteralUnionForBindingElement.ts b/tests/cases/compiler/inferStringLiteralUnionForBindingElement.ts new file mode 100644 index 0000000000000..0b8727ea5a0ce --- /dev/null +++ b/tests/cases/compiler/inferStringLiteralUnionForBindingElement.ts @@ -0,0 +1,21 @@ +declare function func(arg: { keys: T[] }): { readonly keys: T[]; readonly firstKey: T; }; + +function func1() { + const { firstKey } = func({keys: ["aa", "bb"]}) + const a: "aa" | "bb" = firstKey; + + const { keys } = func({keys: ["aa", "bb"]}) + const b: ("aa" | "bb")[] = keys; +} + +function func2() { + const { keys, firstKey } = func({keys: ["aa", "bb"]}) + const a: "aa" | "bb" = firstKey; + const b: ("aa" | "bb")[] = keys; +} + +function func3() { + const x = func({keys: ["aa", "bb"]}) + const a: "aa" | "bb" = x.firstKey; + const b: ("aa" | "bb")[] = x.keys; +}