Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (isArrayType(t) || (t.flags & TypeFlags.Any) && (constraint = getConstraintOfTypeParameter(typeVariable)) && everyType(constraint, or(isArrayType, isTupleType))) {
if (isArrayType(t) || (t.flags & TypeFlags.Any) && (constraint = getConstraintOfTypeParameter(typeVariable)) && everyType(constraint, isArrayOrTupleLikeType)) {

? Or is including the x-like types (length properties and 0 properties) going to mess up the behavior in a case we care about? I'm thinking passing any to a T extends {length: number} may also justify an array mapping rather than a string index signature.

@DanielRosenwasser DanielRosenwasser Oct 5, 2021

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about the tuple-like type thing, and that's one concern - I'm already not 100% convinced that that would always be desirable. The bigger concern is the implementation of isArrayLikeType:

return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);

which I think would consider any an array-like type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raw any in a constraint position is eagerly replaced with unknown nowadays (so it behaves as a proper top type constraint and not as an odd anyish thing) so it shouldn't be an issue I don't think.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that might be fine, but for now there's some precedent in getResolvedApparentTypeOfMappedType for the current behavior.

return instantiateMappedArrayType(t, type, prependTypeMapping(typeVariable, t, mapper));
}
if (isGenericTupleType(t)) {
Expand Down
22 changes: 20 additions & 2 deletions tests/baselines/reference/mappedTypeWithAny.errors.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
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(36,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 (1 errors) ====
==== tests/cases/conformance/types/mapped/mappedTypeWithAny.ts (2 errors) ====
type Item = { value: string };
type ItemMap<T> = { [P in keyof T]: Item };

Expand All @@ -28,4 +29,21 @@ 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 an array-like type.
type Arrayish<T extends unknown[]> = { [K in keyof T]: T[K] };
type Objectish<T extends unknown> = { [K in keyof T]: T[K] };

function bar(arrayish: Arrayish<any>, objectish: Objectish<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.
}

declare function stringifyArray<T extends readonly any[]>(arr: T): { -readonly [K in keyof T]: string };
let abc: any[] = stringifyArray(void 0 as any);
34 changes: 33 additions & 1 deletion tests/baselines/reference/mappedTypeWithAny.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +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 an array-like type.
type Arrayish<T extends unknown[]> = { [K in keyof T]: T[K] };
type Objectish<T extends unknown> = { [K in keyof T]: T[K] };

function bar(arrayish: Arrayish<any>, objectish: Objectish<any>) {
let arr: any[];
arr = arrayish;
arr = objectish;
}

declare function stringifyArray<T extends readonly any[]>(arr: T): { -readonly [K in keyof T]: string };
let abc: any[] = stringifyArray(void 0 as any);

//// [mappedTypeWithAny.js]
"use strict";
for (var id in z) {
var data = z[id];
var x = data.notAValue; // Error
}
function bar(arrayish, objectish) {
var arr;
arr = arrayish;
arr = objectish;
}
var abc = stringifyArray(void 0);


//// [mappedTypeWithAny.d.ts]
Expand Down Expand Up @@ -58,3 +79,14 @@ 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 function bar(arrayish: Arrayish<any>, objectish: Objectish<any>): void;
declare function stringifyArray<T extends readonly any[]>(arr: T): {
-readonly [K in keyof T]: string;
};
declare let abc: any[];
51 changes: 51 additions & 0 deletions tests/baselines/reference/mappedTypeWithAny.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,54 @@ 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 an array-like type.
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))

function bar(arrayish: Arrayish<any>, objectish: Objectish<any>) {
>bar : Symbol(bar, Decl(mappedTypeWithAny.ts, 30, 61))
>arrayish : Symbol(arrayish, Decl(mappedTypeWithAny.ts, 32, 13))
>Arrayish : Symbol(Arrayish, Decl(mappedTypeWithAny.ts, 23, 1))
>objectish : Symbol(objectish, Decl(mappedTypeWithAny.ts, 32, 37))
>Objectish : Symbol(Objectish, Decl(mappedTypeWithAny.ts, 29, 62))

let arr: any[];
>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 33, 7))

arr = arrayish;
>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 33, 7))
>arrayish : Symbol(arrayish, Decl(mappedTypeWithAny.ts, 32, 13))

arr = objectish;
>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 33, 7))
>objectish : Symbol(objectish, Decl(mappedTypeWithAny.ts, 32, 37))
}

declare function stringifyArray<T extends readonly any[]>(arr: T): { -readonly [K in keyof T]: string };
>stringifyArray : Symbol(stringifyArray, Decl(mappedTypeWithAny.ts, 36, 1))
>T : Symbol(T, Decl(mappedTypeWithAny.ts, 38, 32))
>arr : Symbol(arr, Decl(mappedTypeWithAny.ts, 38, 58))
>T : Symbol(T, Decl(mappedTypeWithAny.ts, 38, 32))
>K : Symbol(K, Decl(mappedTypeWithAny.ts, 38, 80))
>T : Symbol(T, Decl(mappedTypeWithAny.ts, 38, 32))

let abc: any[] = stringifyArray(void 0 as any);
>abc : Symbol(abc, Decl(mappedTypeWithAny.ts, 39, 3))
>stringifyArray : Symbol(stringifyArray, Decl(mappedTypeWithAny.ts, 36, 1))

41 changes: 41 additions & 0 deletions tests/baselines/reference/mappedTypeWithAny.types
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,44 @@ 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 an array-like type.
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>

function bar(arrayish: Arrayish<any>, objectish: Objectish<any>) {
>bar : (arrayish: Arrayish<any>, objectish: Objectish<any>) => void
>arrayish : any[]
>objectish : 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>
}

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

19 changes: 19 additions & 0 deletions tests/baselines/reference/promiseAllOnAny01.symbols
Original file line number Diff line number Diff line change
@@ -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 changes: 21 additions & 0 deletions tests/baselines/reference/promiseAllOnAny01.types
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions tests/cases/compiler/promiseAllOnAny01.ts
Original file line number Diff line number Diff line change
@@ -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;
}
16 changes: 16 additions & 0 deletions tests/cases/conformance/types/mapped/mappedTypeWithAny.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,19 @@ 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 an array-like type.
type Arrayish<T extends unknown[]> = { [K in keyof T]: T[K] };
type Objectish<T extends unknown> = { [K in keyof T]: T[K] };

function bar(arrayish: Arrayish<any>, objectish: Objectish<any>) {
let arr: any[];
arr = arrayish;
arr = objectish;
}

declare function stringifyArray<T extends readonly any[]>(arr: T): { -readonly [K in keyof T]: string };
let abc: any[] = stringifyArray(void 0 as any);