diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1af122e4e3b4e..043745c7c5c28 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16515,7 +16515,8 @@ namespace ts { return mapTypeWithAlias(getReducedType(mappedTypeVariable), t => { if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType && !isErrorType(t)) { if (!type.declaration.nameType) { - if (isArrayType(t)) { + let constraint; + if (isArrayType(t) || (t.flags & TypeFlags.Any) && (constraint = getConstraintOfTypeParameter(typeVariable)) && everyType(constraint, or(isArrayType, isTupleType))) { return instantiateMappedArrayType(t, type, prependTypeMapping(typeVariable, t, mapper)); } if (isGenericTupleType(t)) { diff --git a/tests/baselines/reference/mappedTypeWithAny.errors.txt b/tests/baselines/reference/mappedTypeWithAny.errors.txt index 0ce53e5044d56..77a1e3e81a167 100644 --- a/tests/baselines/reference/mappedTypeWithAny.errors.txt +++ b/tests/baselines/reference/mappedTypeWithAny.errors.txt @@ -1,7 +1,11 @@ tests/cases/conformance/types/mapped/mappedTypeWithAny.ts(23,16): error TS2339: Property 'notAValue' does not exist on type 'Data'. +tests/cases/conformance/types/mapped/mappedTypeWithAny.ts(45,5): error TS2740: Type 'Objectish' is missing the following properties from type 'any[]': length, pop, push, concat, and 16 more. +tests/cases/conformance/types/mapped/mappedTypeWithAny.ts(46,5): error TS2322: Type 'Objectish' is not assignable to type 'any[]'. +tests/cases/conformance/types/mapped/mappedTypeWithAny.ts(53,5): error TS2322: Type 'string[]' is not assignable to type '[any, any]'. + Target requires 2 element(s) but source may have fewer. -==== tests/cases/conformance/types/mapped/mappedTypeWithAny.ts (1 errors) ==== +==== tests/cases/conformance/types/mapped/mappedTypeWithAny.ts (4 errors) ==== type Item = { value: string }; type ItemMap = { [P in keyof T]: Item }; @@ -28,4 +32,39 @@ tests/cases/conformance/types/mapped/mappedTypeWithAny.ts(23,16): error TS2339: ~~~~~~~~~ !!! error TS2339: Property 'notAValue' does not exist on type 'Data'. } - \ No newline at end of file + + // Issue #46169. + // We want mapped types whose constraint is `keyof T` to + // map over `any` differently, depending on whether `T` + // is constrained to array and tuple types. + type Arrayish = { [K in keyof T]: T[K] }; + type Objectish = { [K in keyof T]: T[K] }; + + // When a mapped type whose constraint is `keyof T` is instantiated, + // `T` may be instantiated with a `U` which is constrained to + // array and tuple types. *Ideally*, when `U` is later instantiated with `any`, + // the result should also be some sort of array; however, at the moment we don't seem + // to have an easy way to preserve that information. More than just that, it would be + // inconsistent for two instantiations of `Objectish` to produce different outputs + // depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`. + type IndirectArrayish = Objectish; + + function bar(arrayish: Arrayish, objectish: Objectish, indirectArrayish: IndirectArrayish) { + let arr: any[]; + arr = arrayish; + arr = objectish; + ~~~ +!!! error TS2740: Type 'Objectish' is missing the following properties from type 'any[]': length, pop, push, concat, and 16 more. + arr = indirectArrayish; + ~~~ +!!! error TS2322: Type 'Objectish' is not assignable to type 'any[]'. + } + + declare function stringifyArray(arr: T): { -readonly [K in keyof T]: string }; + let abc: any[] = stringifyArray(void 0 as any); + + declare function stringifyPair(arr: T): { -readonly [K in keyof T]: string }; + let def: [any, any] = stringifyPair(void 0 as any); + ~~~ +!!! error TS2322: Type 'string[]' is not assignable to type '[any, any]'. +!!! error TS2322: Target requires 2 element(s) but source may have fewer. \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeWithAny.js b/tests/baselines/reference/mappedTypeWithAny.js index 1e5c662310ca8..a36e8fa3cd917 100644 --- a/tests/baselines/reference/mappedTypeWithAny.js +++ b/tests/baselines/reference/mappedTypeWithAny.js @@ -23,7 +23,35 @@ for (let id in z) { let data = z[id]; let x = data.notAValue; // Error } - + +// Issue #46169. +// We want mapped types whose constraint is `keyof T` to +// map over `any` differently, depending on whether `T` +// is constrained to array and tuple types. +type Arrayish = { [K in keyof T]: T[K] }; +type Objectish = { [K in keyof T]: T[K] }; + +// When a mapped type whose constraint is `keyof T` is instantiated, +// `T` may be instantiated with a `U` which is constrained to +// array and tuple types. *Ideally*, when `U` is later instantiated with `any`, +// the result should also be some sort of array; however, at the moment we don't seem +// to have an easy way to preserve that information. More than just that, it would be +// inconsistent for two instantiations of `Objectish` to produce different outputs +// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`. +type IndirectArrayish = Objectish; + +function bar(arrayish: Arrayish, objectish: Objectish, indirectArrayish: IndirectArrayish) { + let arr: any[]; + arr = arrayish; + arr = objectish; + arr = indirectArrayish; +} + +declare function stringifyArray(arr: T): { -readonly [K in keyof T]: string }; +let abc: any[] = stringifyArray(void 0 as any); + +declare function stringifyPair(arr: T): { -readonly [K in keyof T]: string }; +let def: [any, any] = stringifyPair(void 0 as any); //// [mappedTypeWithAny.js] "use strict"; @@ -31,6 +59,14 @@ for (var id in z) { var data = z[id]; var x = data.notAValue; // Error } +function bar(arrayish, objectish, indirectArrayish) { + var arr; + arr = arrayish; + arr = objectish; + arr = indirectArrayish; +} +var abc = stringifyArray(void 0); +var def = stringifyPair(void 0); //// [mappedTypeWithAny.d.ts] @@ -58,3 +94,19 @@ declare type StrictDataMap = { [P in keyof T]: Data; }; declare let z: StrictDataMap; +declare type Arrayish = { + [K in keyof T]: T[K]; +}; +declare type Objectish = { + [K in keyof T]: T[K]; +}; +declare type IndirectArrayish = Objectish; +declare function bar(arrayish: Arrayish, objectish: Objectish, indirectArrayish: IndirectArrayish): void; +declare function stringifyArray(arr: T): { + -readonly [K in keyof T]: string; +}; +declare let abc: any[]; +declare function stringifyPair(arr: T): { + -readonly [K in keyof T]: string; +}; +declare let def: [any, any]; diff --git a/tests/baselines/reference/mappedTypeWithAny.symbols b/tests/baselines/reference/mappedTypeWithAny.symbols index 2c204d4b1ef27..6b411af1f4529 100644 --- a/tests/baselines/reference/mappedTypeWithAny.symbols +++ b/tests/baselines/reference/mappedTypeWithAny.symbols @@ -69,3 +69,85 @@ for (let id in z) { >data : Symbol(data, Decl(mappedTypeWithAny.ts, 21, 5)) } +// Issue #46169. +// We want mapped types whose constraint is `keyof T` to +// map over `any` differently, depending on whether `T` +// is constrained to array and tuple types. +type Arrayish = { [K in keyof T]: T[K] }; +>Arrayish : Symbol(Arrayish, Decl(mappedTypeWithAny.ts, 23, 1)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 29, 14)) +>K : Symbol(K, Decl(mappedTypeWithAny.ts, 29, 40)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 29, 14)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 29, 14)) +>K : Symbol(K, Decl(mappedTypeWithAny.ts, 29, 40)) + +type Objectish = { [K in keyof T]: T[K] }; +>Objectish : Symbol(Objectish, Decl(mappedTypeWithAny.ts, 29, 62)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 30, 15)) +>K : Symbol(K, Decl(mappedTypeWithAny.ts, 30, 39)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 30, 15)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 30, 15)) +>K : Symbol(K, Decl(mappedTypeWithAny.ts, 30, 39)) + +// When a mapped type whose constraint is `keyof T` is instantiated, +// `T` may be instantiated with a `U` which is constrained to +// array and tuple types. *Ideally*, when `U` is later instantiated with `any`, +// the result should also be some sort of array; however, at the moment we don't seem +// to have an easy way to preserve that information. More than just that, it would be +// inconsistent for two instantiations of `Objectish` to produce different outputs +// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`. +type IndirectArrayish = Objectish; +>IndirectArrayish : Symbol(IndirectArrayish, Decl(mappedTypeWithAny.ts, 30, 61)) +>U : Symbol(U, Decl(mappedTypeWithAny.ts, 39, 22)) +>Objectish : Symbol(Objectish, Decl(mappedTypeWithAny.ts, 29, 62)) +>U : Symbol(U, Decl(mappedTypeWithAny.ts, 39, 22)) + +function bar(arrayish: Arrayish, objectish: Objectish, indirectArrayish: IndirectArrayish) { +>bar : Symbol(bar, Decl(mappedTypeWithAny.ts, 39, 58)) +>arrayish : Symbol(arrayish, Decl(mappedTypeWithAny.ts, 41, 13)) +>Arrayish : Symbol(Arrayish, Decl(mappedTypeWithAny.ts, 23, 1)) +>objectish : Symbol(objectish, Decl(mappedTypeWithAny.ts, 41, 37)) +>Objectish : Symbol(Objectish, Decl(mappedTypeWithAny.ts, 29, 62)) +>indirectArrayish : Symbol(indirectArrayish, Decl(mappedTypeWithAny.ts, 41, 64)) +>IndirectArrayish : Symbol(IndirectArrayish, Decl(mappedTypeWithAny.ts, 30, 61)) + + let arr: any[]; +>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 42, 7)) + + arr = arrayish; +>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 42, 7)) +>arrayish : Symbol(arrayish, Decl(mappedTypeWithAny.ts, 41, 13)) + + arr = objectish; +>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 42, 7)) +>objectish : Symbol(objectish, Decl(mappedTypeWithAny.ts, 41, 37)) + + arr = indirectArrayish; +>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 42, 7)) +>indirectArrayish : Symbol(indirectArrayish, Decl(mappedTypeWithAny.ts, 41, 64)) +} + +declare function stringifyArray(arr: T): { -readonly [K in keyof T]: string }; +>stringifyArray : Symbol(stringifyArray, Decl(mappedTypeWithAny.ts, 46, 1)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 48, 32)) +>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 48, 58)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 48, 32)) +>K : Symbol(K, Decl(mappedTypeWithAny.ts, 48, 80)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 48, 32)) + +let abc: any[] = stringifyArray(void 0 as any); +>abc : Symbol(abc, Decl(mappedTypeWithAny.ts, 49, 3)) +>stringifyArray : Symbol(stringifyArray, Decl(mappedTypeWithAny.ts, 46, 1)) + +declare function stringifyPair(arr: T): { -readonly [K in keyof T]: string }; +>stringifyPair : Symbol(stringifyPair, Decl(mappedTypeWithAny.ts, 49, 47)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 51, 31)) +>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 51, 62)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 51, 31)) +>K : Symbol(K, Decl(mappedTypeWithAny.ts, 51, 84)) +>T : Symbol(T, Decl(mappedTypeWithAny.ts, 51, 31)) + +let def: [any, any] = stringifyPair(void 0 as any); +>def : Symbol(def, Decl(mappedTypeWithAny.ts, 52, 3)) +>stringifyPair : Symbol(stringifyPair, Decl(mappedTypeWithAny.ts, 49, 47)) + diff --git a/tests/baselines/reference/mappedTypeWithAny.types b/tests/baselines/reference/mappedTypeWithAny.types index c95a17ab43b51..f8eb3abf08708 100644 --- a/tests/baselines/reference/mappedTypeWithAny.types +++ b/tests/baselines/reference/mappedTypeWithAny.types @@ -56,3 +56,72 @@ for (let id in z) { >notAValue : any } +// Issue #46169. +// We want mapped types whose constraint is `keyof T` to +// map over `any` differently, depending on whether `T` +// is constrained to array and tuple types. +type Arrayish = { [K in keyof T]: T[K] }; +>Arrayish : Arrayish + +type Objectish = { [K in keyof T]: T[K] }; +>Objectish : Objectish + +// When a mapped type whose constraint is `keyof T` is instantiated, +// `T` may be instantiated with a `U` which is constrained to +// array and tuple types. *Ideally*, when `U` is later instantiated with `any`, +// the result should also be some sort of array; however, at the moment we don't seem +// to have an easy way to preserve that information. More than just that, it would be +// inconsistent for two instantiations of `Objectish` to produce different outputs +// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`. +type IndirectArrayish = Objectish; +>IndirectArrayish : Objectish + +function bar(arrayish: Arrayish, objectish: Objectish, indirectArrayish: IndirectArrayish) { +>bar : (arrayish: Arrayish, objectish: Objectish, indirectArrayish: IndirectArrayish) => void +>arrayish : any[] +>objectish : Objectish +>indirectArrayish : Objectish + + let arr: any[]; +>arr : any[] + + arr = arrayish; +>arr = arrayish : any[] +>arr : any[] +>arrayish : any[] + + arr = objectish; +>arr = objectish : Objectish +>arr : any[] +>objectish : Objectish + + arr = indirectArrayish; +>arr = indirectArrayish : Objectish +>arr : any[] +>indirectArrayish : Objectish +} + +declare function stringifyArray(arr: T): { -readonly [K in keyof T]: string }; +>stringifyArray : (arr: T) => { -readonly [K in keyof T]: string; } +>arr : T + +let abc: any[] = stringifyArray(void 0 as any); +>abc : any[] +>stringifyArray(void 0 as any) : string[] +>stringifyArray : (arr: T) => { -readonly [K in keyof T]: string; } +>void 0 as any : any +>void 0 : undefined +>0 : 0 + +declare function stringifyPair(arr: T): { -readonly [K in keyof T]: string }; +>stringifyPair : (arr: T) => { -readonly [K in keyof T]: string; } +>arr : T + +let def: [any, any] = stringifyPair(void 0 as any); +>def : [any, any] +>stringifyPair(void 0 as any) : string[] +>stringifyPair : (arr: T) => { -readonly [K in keyof T]: string; } +>void 0 as any : any +>void 0 : undefined +>0 : 0 + diff --git a/tests/baselines/reference/promiseAllOnAny01.symbols b/tests/baselines/reference/promiseAllOnAny01.symbols new file mode 100644 index 0000000000000..3a31306889f2b --- /dev/null +++ b/tests/baselines/reference/promiseAllOnAny01.symbols @@ -0,0 +1,19 @@ +=== tests/cases/compiler/promiseAllOnAny01.ts === +async function foo(x: any) { +>foo : Symbol(foo, Decl(promiseAllOnAny01.ts, 0, 0)) +>x : Symbol(x, Decl(promiseAllOnAny01.ts, 0, 19)) + + let abc = await Promise.all(x); +>abc : Symbol(abc, Decl(promiseAllOnAny01.ts, 1, 7)) +>Promise.all : Symbol(PromiseConstructor.all, Decl(lib.es2015.promise.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>all : Symbol(PromiseConstructor.all, Decl(lib.es2015.promise.d.ts, --, --)) +>x : Symbol(x, Decl(promiseAllOnAny01.ts, 0, 19)) + + let result: any[] = abc; +>result : Symbol(result, Decl(promiseAllOnAny01.ts, 2, 7)) +>abc : Symbol(abc, Decl(promiseAllOnAny01.ts, 1, 7)) + + return result; +>result : Symbol(result, Decl(promiseAllOnAny01.ts, 2, 7)) +} diff --git a/tests/baselines/reference/promiseAllOnAny01.types b/tests/baselines/reference/promiseAllOnAny01.types new file mode 100644 index 0000000000000..c0431773e66d5 --- /dev/null +++ b/tests/baselines/reference/promiseAllOnAny01.types @@ -0,0 +1,21 @@ +=== tests/cases/compiler/promiseAllOnAny01.ts === +async function foo(x: any) { +>foo : (x: any) => Promise +>x : any + + let abc = await Promise.all(x); +>abc : any[] +>await Promise.all(x) : any[] +>Promise.all(x) : Promise +>Promise.all : (values: T) => Promise<{ -readonly [P in keyof T]: Awaited; }> +>Promise : PromiseConstructor +>all : (values: T) => Promise<{ -readonly [P in keyof T]: Awaited; }> +>x : any + + let result: any[] = abc; +>result : any[] +>abc : any[] + + return result; +>result : any[] +} diff --git a/tests/cases/compiler/promiseAllOnAny01.ts b/tests/cases/compiler/promiseAllOnAny01.ts new file mode 100644 index 0000000000000..75d3b2d09026d --- /dev/null +++ b/tests/cases/compiler/promiseAllOnAny01.ts @@ -0,0 +1,8 @@ +// @noEmit: true +// @lib: es5,es2015.promise + +async function foo(x: any) { + let abc = await Promise.all(x); + let result: any[] = abc; + return result; +} \ No newline at end of file diff --git a/tests/cases/conformance/types/mapped/mappedTypeWithAny.ts b/tests/cases/conformance/types/mapped/mappedTypeWithAny.ts index f8b6f8a39b7d2..41eaa5945c91c 100644 --- a/tests/cases/conformance/types/mapped/mappedTypeWithAny.ts +++ b/tests/cases/conformance/types/mapped/mappedTypeWithAny.ts @@ -25,3 +25,32 @@ for (let id in z) { let data = z[id]; let x = data.notAValue; // Error } + +// Issue #46169. +// We want mapped types whose constraint is `keyof T` to +// map over `any` differently, depending on whether `T` +// is constrained to array and tuple types. +type Arrayish = { [K in keyof T]: T[K] }; +type Objectish = { [K in keyof T]: T[K] }; + +// When a mapped type whose constraint is `keyof T` is instantiated, +// `T` may be instantiated with a `U` which is constrained to +// array and tuple types. *Ideally*, when `U` is later instantiated with `any`, +// the result should also be some sort of array; however, at the moment we don't seem +// to have an easy way to preserve that information. More than just that, it would be +// inconsistent for two instantiations of `Objectish` to produce different outputs +// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`. +type IndirectArrayish = Objectish; + +function bar(arrayish: Arrayish, objectish: Objectish, indirectArrayish: IndirectArrayish) { + let arr: any[]; + arr = arrayish; + arr = objectish; + arr = indirectArrayish; +} + +declare function stringifyArray(arr: T): { -readonly [K in keyof T]: string }; +let abc: any[] = stringifyArray(void 0 as any); + +declare function stringifyPair(arr: T): { -readonly [K in keyof T]: string }; +let def: [any, any] = stringifyPair(void 0 as any); \ No newline at end of file