Check for array types when instantiating mapped type constraints with any
(#46218)
* Added/updated tests.
* Accepted baselines.
* Update test.
* Update instantiateMappedType to work specially when 'any' replaced an array.
* Accepted baselines.
* Ensure check works when constraint is a union of arrayish types, just like in `Promise.all`.
* Accepted baselines.
* Update test for indirect instantiation of a mapped type.
* Accepted baselines.
* Update test comment.
* Accepted baselines.
* Added tuple test case.
* Accepted baselines.
* Don't add special behavior for tuples.
* Accepted baselines.
* Revert "Don't add special behavior for tuples."
This reverts commit f01ae16e65
.
* Accepted baselines.
This commit is contained in:
parent
abfd537503
commit
f494742ce9
|
@ -16570,7 +16570,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)) {
|
||||
|
|
|
@ -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<any>' 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<any>' 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<T> = { [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'.
|
||||
}
|
||||
|
||||
|
||||
// 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<T extends unknown[]> = { [K in keyof T]: T[K] };
|
||||
type Objectish<T extends unknown> = { [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<any>` to produce different outputs
|
||||
// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`.
|
||||
type IndirectArrayish<U extends unknown[]> = Objectish<U>;
|
||||
|
||||
function bar(arrayish: Arrayish<any>, objectish: Objectish<any>, indirectArrayish: IndirectArrayish<any>) {
|
||||
let arr: any[];
|
||||
arr = arrayish;
|
||||
arr = objectish;
|
||||
~~~
|
||||
!!! error TS2740: Type 'Objectish<any>' is missing the following properties from type 'any[]': length, pop, push, concat, and 16 more.
|
||||
arr = indirectArrayish;
|
||||
~~~
|
||||
!!! error TS2322: Type 'Objectish<any>' is not assignable to type 'any[]'.
|
||||
}
|
||||
|
||||
declare function stringifyArray<T extends readonly any[]>(arr: T): { -readonly [K in keyof T]: string };
|
||||
let abc: any[] = stringifyArray(void 0 as any);
|
||||
|
||||
declare function stringifyPair<T extends readonly [any, any]>(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.
|
|
@ -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<T extends unknown[]> = { [K in keyof T]: T[K] };
|
||||
type Objectish<T extends unknown> = { [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<any>` to produce different outputs
|
||||
// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`.
|
||||
type IndirectArrayish<U extends unknown[]> = Objectish<U>;
|
||||
|
||||
function bar(arrayish: Arrayish<any>, objectish: Objectish<any>, indirectArrayish: IndirectArrayish<any>) {
|
||||
let arr: any[];
|
||||
arr = arrayish;
|
||||
arr = objectish;
|
||||
arr = indirectArrayish;
|
||||
}
|
||||
|
||||
declare function stringifyArray<T extends readonly any[]>(arr: T): { -readonly [K in keyof T]: string };
|
||||
let abc: any[] = stringifyArray(void 0 as any);
|
||||
|
||||
declare function stringifyPair<T extends readonly [any, any]>(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<T> = {
|
|||
[P in keyof T]: Data;
|
||||
};
|
||||
declare let z: StrictDataMap<any>;
|
||||
declare type Arrayish<T extends unknown[]> = {
|
||||
[K in keyof T]: T[K];
|
||||
};
|
||||
declare type Objectish<T extends unknown> = {
|
||||
[K in keyof T]: T[K];
|
||||
};
|
||||
declare type IndirectArrayish<U extends unknown[]> = Objectish<U>;
|
||||
declare function bar(arrayish: Arrayish<any>, objectish: Objectish<any>, indirectArrayish: IndirectArrayish<any>): void;
|
||||
declare function stringifyArray<T extends readonly any[]>(arr: T): {
|
||||
-readonly [K in keyof T]: string;
|
||||
};
|
||||
declare let abc: any[];
|
||||
declare function stringifyPair<T extends readonly [any, any]>(arr: T): {
|
||||
-readonly [K in keyof T]: string;
|
||||
};
|
||||
declare let def: [any, any];
|
||||
|
|
|
@ -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<T extends unknown[]> = { [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<T extends unknown> = { [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<any>` to produce different outputs
|
||||
// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`.
|
||||
type IndirectArrayish<U extends unknown[]> = Objectish<U>;
|
||||
>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<any>, objectish: Objectish<any>, indirectArrayish: IndirectArrayish<any>) {
|
||||
>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<T extends readonly any[]>(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<T extends readonly [any, any]>(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))
|
||||
|
||||
|
|
|
@ -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<T extends unknown[]> = { [K in keyof T]: T[K] };
|
||||
>Arrayish : Arrayish<T>
|
||||
|
||||
type Objectish<T extends unknown> = { [K in keyof T]: T[K] };
|
||||
>Objectish : Objectish<T>
|
||||
|
||||
// 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<any>` to produce different outputs
|
||||
// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`.
|
||||
type IndirectArrayish<U extends unknown[]> = Objectish<U>;
|
||||
>IndirectArrayish : Objectish<U>
|
||||
|
||||
function bar(arrayish: Arrayish<any>, objectish: Objectish<any>, indirectArrayish: IndirectArrayish<any>) {
|
||||
>bar : (arrayish: Arrayish<any>, objectish: Objectish<any>, indirectArrayish: IndirectArrayish<any>) => void
|
||||
>arrayish : any[]
|
||||
>objectish : Objectish<any>
|
||||
>indirectArrayish : Objectish<any>
|
||||
|
||||
let arr: any[];
|
||||
>arr : any[]
|
||||
|
||||
arr = arrayish;
|
||||
>arr = arrayish : any[]
|
||||
>arr : any[]
|
||||
>arrayish : any[]
|
||||
|
||||
arr = objectish;
|
||||
>arr = objectish : Objectish<any>
|
||||
>arr : any[]
|
||||
>objectish : Objectish<any>
|
||||
|
||||
arr = indirectArrayish;
|
||||
>arr = indirectArrayish : Objectish<any>
|
||||
>arr : any[]
|
||||
>indirectArrayish : Objectish<any>
|
||||
}
|
||||
|
||||
declare function stringifyArray<T extends readonly any[]>(arr: T): { -readonly [K in keyof T]: string };
|
||||
>stringifyArray : <T extends readonly any[]>(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 : <T extends readonly any[]>(arr: T) => { -readonly [K in keyof T]: string; }
|
||||
>void 0 as any : any
|
||||
>void 0 : undefined
|
||||
>0 : 0
|
||||
|
||||
declare function stringifyPair<T extends readonly [any, any]>(arr: T): { -readonly [K in keyof T]: string };
|
||||
>stringifyPair : <T extends readonly [any, any]>(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 : <T extends readonly [any, any]>(arr: T) => { -readonly [K in keyof T]: string; }
|
||||
>void 0 as any : any
|
||||
>void 0 : undefined
|
||||
>0 : 0
|
||||
|
||||
|
|
19
tests/baselines/reference/promiseAllOnAny01.symbols
Normal file
19
tests/baselines/reference/promiseAllOnAny01.symbols
Normal file
|
@ -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))
|
||||
}
|
21
tests/baselines/reference/promiseAllOnAny01.types
Normal file
21
tests/baselines/reference/promiseAllOnAny01.types
Normal file
|
@ -0,0 +1,21 @@
|
|||
=== tests/cases/compiler/promiseAllOnAny01.ts ===
|
||||
async function foo(x: any) {
|
||||
>foo : (x: any) => Promise<any[]>
|
||||
>x : any
|
||||
|
||||
let abc = await Promise.all(x);
|
||||
>abc : any[]
|
||||
>await Promise.all(x) : any[]
|
||||
>Promise.all(x) : Promise<any[]>
|
||||
>Promise.all : <T extends readonly unknown[] | []>(values: T) => Promise<{ -readonly [P in keyof T]: Awaited<T[P]>; }>
|
||||
>Promise : PromiseConstructor
|
||||
>all : <T extends readonly unknown[] | []>(values: T) => Promise<{ -readonly [P in keyof T]: Awaited<T[P]>; }>
|
||||
>x : any
|
||||
|
||||
let result: any[] = abc;
|
||||
>result : any[]
|
||||
>abc : any[]
|
||||
|
||||
return result;
|
||||
>result : any[]
|
||||
}
|
8
tests/cases/compiler/promiseAllOnAny01.ts
Normal file
8
tests/cases/compiler/promiseAllOnAny01.ts
Normal file
|
@ -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;
|
||||
}
|
|
@ -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<T extends unknown[]> = { [K in keyof T]: T[K] };
|
||||
type Objectish<T extends unknown> = { [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<any>` to produce different outputs
|
||||
// depending on the usage-site. As a result, `IndirectArrayish` does not act like `Arrayish`.
|
||||
type IndirectArrayish<U extends unknown[]> = Objectish<U>;
|
||||
|
||||
function bar(arrayish: Arrayish<any>, objectish: Objectish<any>, indirectArrayish: IndirectArrayish<any>) {
|
||||
let arr: any[];
|
||||
arr = arrayish;
|
||||
arr = objectish;
|
||||
arr = indirectArrayish;
|
||||
}
|
||||
|
||||
declare function stringifyArray<T extends readonly any[]>(arr: T): { -readonly [K in keyof T]: string };
|
||||
let abc: any[] = stringifyArray(void 0 as any);
|
||||
|
||||
declare function stringifyPair<T extends readonly [any, any]>(arr: T): { -readonly [K in keyof T]: string };
|
||||
let def: [any, any] = stringifyPair(void 0 as any);
|
Loading…
Reference in a new issue