-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add numeric constraint to type parameter of mapped types with name type and array constraints #55774
Add numeric constraint to type parameter of mapped types with name type and array constraints #55774
Changes from 2 commits
c28fb9e
32ca0d3
67c8a8f
c66fd92
6876e98
964b339
863a1d1
51c588c
e33988d
b5c12ee
b55610e
0318886
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13583,7 +13583,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |
const include = keyofStringsOnly ? TypeFlags.StringLiteral : TypeFlags.StringOrNumberLiteralOrUnique; | ||
if (isMappedTypeWithKeyofConstraintDeclaration(type)) { | ||
// We have a { [P in keyof T]: X } | ||
forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, keyofStringsOnly, addMemberForKeyType); | ||
if (nameType && isTupleType(modifiersType)) { | ||
forEachType(getUnionType(getElementTypes(modifiersType).map((_, i) => getStringLiteralType("" + i))), addMemberForKeyType); | ||
} | ||
else if (isArrayType(modifiersType)) { | ||
addMemberForKeyType(numberType); | ||
} | ||
else { | ||
forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, keyofStringsOnly, addMemberForKeyType); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, should this change be made inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are good concerns and I need to take a deeper look at this. This requires more focus time from me so it will take some time before I get to it - but I will certainly circle back here and let you know once I have some conclusions for this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We discussed the issue this PR addresses at design meeting today: #56233 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change here is needed. Usually, tuple types are immediately~ instantiated by But with the introduction of this change, we introduce the ability to map a tuple type into an object type. So resolving of output's members stays deferred~ and, for the first time, we get a tuple There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I understand your comment, but here's where my understanding is at so far:
I think I don't understand why you said "we need to limit the mapping accordingly". The consequences of not limiting the mapping are that, if you want your mapped type over a tuple type to behave nicely like in the issue's example, you may need to add a constraint to the constraint type, e.g. Sorry if some of this is repeated from other comments, I'm trying to make sure I understand things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This matches my understanding 👍
If I understand correctly a mapped type can still be homomorphic - even if it has a name type. I confirmed that a type like this pass through type Test<T> = { [K in keyof T as K & string]: T[K] } It definitely dilutes the name though since it's not exactly that the output here will be of the same shape 🤷 But property symbols links, property modifiers, are nicely preserved when the name type is just filtering
The rest here matches my understanding 👍
Yes - this is needed to fix/improve the example in the issue. git diff without those linesdiff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index f87f4479fa..d174690ace 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -13703,15 +13703,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const include = keyofStringsOnly ? TypeFlags.StringLiteral : TypeFlags.StringOrNumberLiteralOrUnique;
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
// We have a { [P in keyof T]: X }
- if (isTupleType(modifiersType)) {
- forEachType(getUnionType(getElementTypes(modifiersType).map((_, i) => getStringLiteralType("" + i))), addMemberForKeyType);
- }
- else if (isArrayType(modifiersType)) {
- addMemberForKeyType(numberType);
- }
- else {
- forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, keyofStringsOnly, addMemberForKeyType);
- }
+ forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, keyofStringsOnly, addMemberForKeyType);
}
else {
forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType);
diff --git a/tests/baselines/reference/mappedTypeTupleConstraintTypeParameterInNameType.types b/tests/baselines/reference/mappedTypeTupleConstraintTypeParameterInNameType.types
index 8b80020c7c..c28c1811a7 100644
--- a/tests/baselines/reference/mappedTypeTupleConstraintTypeParameterInNameType.types
+++ b/tests/baselines/reference/mappedTypeTupleConstraintTypeParameterInNameType.types
@@ -80,15 +80,15 @@ const struct1 = structDecoder1.decode(new ArrayBuffer(100));
const v1_1: number = struct1.a;
>v1_1 : number
->struct1.a : number
+>struct1.a : number | bigint
>struct1 : StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]>
->a : number
+>a : number | bigint
const v1_2: bigint = struct1.b;
>v1_2 : bigint
->struct1.b : bigint
+>struct1.b : number | bigint
>struct1 : StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]>
->b : bigint
+>b : number | bigint
declare const descriptor2: [["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]
>descriptor2 : [["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]
@@ -111,19 +111,19 @@ const struct2 = structDecoder2.decode(new ArrayBuffer(100));
const v2_1: number = struct2.a;
>v2_1 : number
->struct2.a : number
+>struct2.a : string | number | bigint
>struct2 : StructTypeFor<[["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]>
->a : number
+>a : string | number | bigint
const v2_2: string = struct2.b;
>v2_2 : string
->struct2.b : string
+>struct2.b : string | number | bigint
>struct2 : StructTypeFor<[["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]>
->b : string
+>b : string | number | bigint
const v2_3: bigint = struct2.c;
>v2_3 : bigint
->struct2.c : bigint
+>struct2.c : string | number | bigint
>struct2 : StructTypeFor<[["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]>
->c : bigint
+>c : string | number | bigint
diff --git a/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty.errors.txt b/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty.errors.txt
index a2f7f389e9..50b1a88109 100644
--- a/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty.errors.txt
+++ b/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty.errors.txt
@@ -1,4 +1,4 @@
-mappedTypeWithAsClauseAndLateBoundProperty.ts(3,1): error TS2741: Property 'length' is missing in type '{ [x: number]: number; [iterator]: () => IterableIterator<number>; [unscopables]: { [x: number]: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }' but required in type 'number[]'.
+mappedTypeWithAsClauseAndLateBoundProperty.ts(3,1): error TS2741: Property 'length' is missing in type '{ [x: number]: number; [iterator]: () => IterableIterator<number>; [unscopables]: { [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }' but required in type 'number[]'.
==== mappedTypeWithAsClauseAndLateBoundProperty.ts (1 errors) ====
@@ -6,6 +6,6 @@ mappedTypeWithAsClauseAndLateBoundProperty.ts(3,1): error TS2741: Property 'leng
declare let src2: { [K in keyof number[] & PropertyKey as Exclude<K, "length">]: (number[])[K] };
tgt2 = src2; // Should error
~~~~
-!!! error TS2741: Property 'length' is missing in type '{ [x: number]: number; [iterator]: () => IterableIterator<number>; [unscopables]: { [x: number]: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }' but required in type 'number[]'.
+!!! error TS2741: Property 'length' is missing in type '{ [x: number]: number; [iterator]: () => IterableIterator<number>; [unscopables]: { [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }' but required in type 'number[]'.
!!! related TS2728 lib.es5.d.ts:--:--: 'length' is declared here.
\ No newline at end of file
diff --git a/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty.types b/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty.types
index a6302b7a11..c8b6485d30 100644
--- a/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty.types
+++ b/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty.types
@@ -5,10 +5,10 @@ declare let tgt2: number[];
>tgt2 : number[]
declare let src2: { [K in keyof number[] & PropertyKey as Exclude<K, "length">]: (number[])[K] };
->src2 : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
+>src2 : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
tgt2 = src2; // Should error
->tgt2 = src2 : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
+>tgt2 = src2 : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
>tgt2 : number[]
->src2 : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
+>src2 : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
diff --git a/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty2.types b/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty2.types
index 457a86874b..56eb6dec96 100644
--- a/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty2.types
+++ b/tests/baselines/reference/mappedTypeWithAsClauseAndLateBoundProperty2.types
@@ -2,8 +2,8 @@
=== mappedTypeWithAsClauseAndLateBoundProperty2.ts ===
export const thing = (null as any as { [K in keyof number[] & PropertyKey as Exclude<K, "length">]: (number[])[K] });
->thing : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
->(null as any as { [K in keyof number[] & PropertyKey as Exclude<K, "length">]: (number[])[K] }) : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
->null as any as { [K in keyof number[] & PropertyKey as Exclude<K, "length">]: (number[])[K] } : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
+>thing : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
+>(null as any as { [K in keyof number[] & PropertyKey as Exclude<K, "length">]: (number[])[K] }) : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
+>null as any as { [K in keyof number[] & PropertyKey as Exclude<K, "length">]: (number[])[K] } : { [x: number]: number; [Symbol.iterator]: () => IterableIterator<number>; [Symbol.unscopables]: { [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; }; toString: () => string; toLocaleString: () => string; pop: () => number; push: (...items: number[]) => number; concat: { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }; join: (separator?: string) => string; reverse: () => number[]; shift: () => number; slice: (start?: number, end?: number) => number[]; sort: (compareFn?: (a: number, b: number) => number) => number[]; splice: { (start: number, deleteCount?: number): number[]; (start: number, deleteCount: number, ...items: number[]): number[]; }; unshift: (...items: number[]) => number; indexOf: (searchElement: number, fromIndex?: number) => number; lastIndexOf: (searchElement: number, fromIndex?: number) => number; every: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }; some: (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean; forEach: (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void; map: <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]; filter: { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }; reduce: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; reduceRight: { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }; find: { <S extends number>(predicate: (value: number, index: number, obj: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; }; findIndex: (predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any) => number; fill: (value: number, start?: number, end?: number) => number[]; copyWithin: (target: number, start: number, end?: number) => number[]; entries: () => IterableIterator<[number, number]>; keys: () => IterableIterator<number>; values: () => IterableIterator<number>; includes: (searchElement: number, fromIndex?: number) => boolean; flatMap: <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]; flat: <A, D extends number = 1>(this: A, depth?: D) => FlatArray<A, D>[]; }
>null as any : any Let's recap what might happen for the simplest case with a fixed-size tuple like With the proposed change It would get even more incorrect with variadic tuples as variadic positions (and positions after them) are not even properties. Their instantiated
Right.
Initially, I got the impression from those meeting notes that the ruling was in favor of this PR. It mentions:
And I treat this case as a homomorphic mapped type (based on what I mentioned above). But now... well, I'm not like super sure what was the ruling of that design meeting. Those notes are a little bit chaotic 😅 There is also a mention of this:
And ye, I guess I agree that this kinda makes sense to some extent but it's also not the case when it comes to the mentioned "special case": type Test<T extends any[]> = { [K in keyof T]: K }
type Result = Test<[boolean, '', ...number[]]> // ["0", "1", ..."2"[]] The numeric signature doesn't come up in the output here. So I still think that the approach that I have used is good and that it fits the bigger picture nicely.
No worries. It's not like my understanding is perfect either. I appreciate the thoroughness of the review. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have an example of what could go wrong if the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
git diffdiff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index f87f4479fa..fc6ee5694b 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -13702,16 +13702,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const templateModifiers = getMappedTypeModifiers(type);
const include = keyofStringsOnly ? TypeFlags.StringLiteral : TypeFlags.StringOrNumberLiteralOrUnique;
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
// We have a { [P in keyof T]: X }
- if (isTupleType(modifiersType)) {
- forEachType(getUnionType(getElementTypes(modifiersType).map((_, i) => getStringLiteralType("" + i))), addMemberForKeyType);
- }
- else if (isArrayType(modifiersType)) {
- addMemberForKeyType(numberType);
- }
- else {
- forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, keyofStringsOnly, addMemberForKeyType);
- }
+ forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, keyofStringsOnly, addMemberForKeyType);
}
else {
forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType);
diff --git a/tests/baselines/reference/mappedTypeTupleConstraintTypeParameterInNameType.types b/tests/baselines/reference/mappedTypeTupleConstraintTypeParameterInNameType.types
index 8b80020c7c..c28c1811a7 100644
--- a/tests/baselines/reference/mappedTypeTupleConstraintTypeParameterInNameType.types
+++ b/tests/baselines/reference/mappedTypeTupleConstraintTypeParameterInNameType.types
@@ -80,15 +80,15 @@ const struct1 = structDecoder1.decode(new ArrayBuffer(100));
const v1_1: number = struct1.a;
>v1_1 : number
->struct1.a : number
+>struct1.a : number | bigint
>struct1 : StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]>
->a : number
+>a : number | bigint
const v1_2: bigint = struct1.b;
>v1_2 : bigint
->struct1.b : bigint
+>struct1.b : number | bigint
>struct1 : StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]>
->b : bigint
+>b : number | bigint
declare const descriptor2: [["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]
>descriptor2 : [["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]
@@ -111,19 +111,19 @@ const struct2 = structDecoder2.decode(new ArrayBuffer(100));
const v2_1: number = struct2.a;
>v2_1 : number
->struct2.a : number
+>struct2.a : string | number | bigint
>struct2 : StructTypeFor<[["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]>
->a : number
+>a : string | number | bigint
const v2_2: string = struct2.b;
>v2_2 : string
->struct2.b : string
+>struct2.b : string | number | bigint
>struct2 : StructTypeFor<[["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]>
->b : string
+>b : string | number | bigint
const v2_3: bigint = struct2.c;
>v2_3 : bigint
->struct2.c : bigint
+>struct2.c : string | number | bigint
>struct2 : StructTypeFor<[["a", Decoder<number>], ["b", Decoder<string>], ...["c", Decoder<bigint>][]]>
->c : bigint
+>c : string | number | bigint There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so it seems like the design meeting discussion didn't cover all the facts.
I think one thing left to potentially address in this PR is to add this change here to the implementation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll take care of the requested changes in the coming days. |
||
} | ||
} | ||
else { | ||
forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType); | ||
|
@@ -15881,7 +15889,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |
} | ||
// Given a homomorphic mapped type { [K in keyof T]: XXX }, where T is constrained to an array or tuple type, in the | ||
// template type XXX, K has an added constraint of number | `${number}`. | ||
else if (type.flags & TypeFlags.TypeParameter && parent.kind === SyntaxKind.MappedType && node === (parent as MappedTypeNode).type) { | ||
else if (type.flags & TypeFlags.TypeParameter && parent.kind === SyntaxKind.MappedType && (node === (parent as MappedTypeNode).type || node === (parent as MappedTypeNode).nameType)) { | ||
const mappedType = getTypeFromTypeNode(parent as TypeNode) as MappedType; | ||
if (getTypeParameterFromMappedType(mappedType) === getActualTypeVariable(type)) { | ||
const typeParameter = getHomomorphicTypeVariable(mappedType); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
//// [tests/cases/compiler/mappedTypeTupleConstraintTypeParameterInNameType.ts] //// | ||
|
||
=== mappedTypeTupleConstraintTypeParameterInNameType.ts === | ||
// based on https://github.com/microsoft/TypeScript/issues/55762 | ||
|
||
declare class Decoder<T> { | ||
>Decoder : Symbol(Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 0, 0)) | ||
>T : Symbol(T, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 2, 22)) | ||
|
||
decode(arrayBuffer: ArrayBuffer): T; | ||
>decode : Symbol(Decoder.decode, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 2, 26)) | ||
>arrayBuffer : Symbol(arrayBuffer, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 3, 9)) | ||
>ArrayBuffer : Symbol(ArrayBuffer, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) | ||
>T : Symbol(T, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 2, 22)) | ||
} | ||
|
||
type ValueTypeOf<T extends Decoder<any>> = T extends Decoder<infer R> | ||
>ValueTypeOf : Symbol(ValueTypeOf, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 4, 1)) | ||
>T : Symbol(T, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 6, 17)) | ||
>Decoder : Symbol(Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 0, 0)) | ||
>T : Symbol(T, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 6, 17)) | ||
>Decoder : Symbol(Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 0, 0)) | ||
>R : Symbol(R, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 6, 66)) | ||
|
||
? R | ||
>R : Symbol(R, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 6, 66)) | ||
|
||
: never; | ||
|
||
type StructDescriptor = ReadonlyArray< | ||
>StructDescriptor : Symbol(StructDescriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 8, 10)) | ||
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --)) | ||
|
||
readonly [key: string, type: Decoder<any>] | ||
>Decoder : Symbol(Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 0, 0)) | ||
|
||
>; | ||
|
||
type StructTypeFor<Descriptor extends StructDescriptor> = { | ||
>StructTypeFor : Symbol(StructTypeFor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 12, 2)) | ||
>Descriptor : Symbol(Descriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 14, 19)) | ||
>StructDescriptor : Symbol(StructDescriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 8, 10)) | ||
|
||
[K in keyof Descriptor as Descriptor[K][0]]: ValueTypeOf<Descriptor[K][1]>; | ||
>K : Symbol(K, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 15, 3)) | ||
>Descriptor : Symbol(Descriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 14, 19)) | ||
>Descriptor : Symbol(Descriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 14, 19)) | ||
>K : Symbol(K, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 15, 3)) | ||
>ValueTypeOf : Symbol(ValueTypeOf, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 4, 1)) | ||
>Descriptor : Symbol(Descriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 14, 19)) | ||
>K : Symbol(K, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 15, 3)) | ||
|
||
}; | ||
|
||
class StructDecoder<const Descriptor extends StructDescriptor> extends Decoder< | ||
>StructDecoder : Symbol(StructDecoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 16, 2)) | ||
>Descriptor : Symbol(Descriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 18, 20)) | ||
>StructDescriptor : Symbol(StructDescriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 8, 10)) | ||
>Decoder : Symbol(Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 0, 0)) | ||
|
||
StructTypeFor<Descriptor> | ||
>StructTypeFor : Symbol(StructTypeFor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 12, 2)) | ||
>Descriptor : Symbol(Descriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 18, 20)) | ||
|
||
> { | ||
constructor(descriptor: Descriptor) { | ||
>descriptor : Symbol(descriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 21, 14)) | ||
>Descriptor : Symbol(Descriptor, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 18, 20)) | ||
|
||
super(); | ||
>super : Symbol(Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 0, 0)) | ||
} | ||
} | ||
|
||
declare const i32Decoder: Decoder<number>; | ||
>i32Decoder : Symbol(i32Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 26, 13)) | ||
>Decoder : Symbol(Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 0, 0)) | ||
|
||
declare const i64Decoder: Decoder<bigint>; | ||
>i64Decoder : Symbol(i64Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 27, 13)) | ||
>Decoder : Symbol(Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 0, 0)) | ||
|
||
const structDecoder = new StructDecoder([ | ||
>structDecoder : Symbol(structDecoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 29, 5)) | ||
>StructDecoder : Symbol(StructDecoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 16, 2)) | ||
|
||
["a", i32Decoder], | ||
>i32Decoder : Symbol(i32Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 26, 13)) | ||
|
||
["b", i64Decoder], | ||
>i64Decoder : Symbol(i64Decoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 27, 13)) | ||
|
||
]); | ||
|
||
const struct = structDecoder.decode(new ArrayBuffer(100)); | ||
>struct : Symbol(struct, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 34, 5)) | ||
>structDecoder.decode : Symbol(Decoder.decode, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 2, 26)) | ||
>structDecoder : Symbol(structDecoder, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 29, 5)) | ||
>decode : Symbol(Decoder.decode, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 2, 26)) | ||
>ArrayBuffer : Symbol(ArrayBuffer, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) | ||
|
||
const v: number = struct.a; | ||
>v : Symbol(v, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 36, 5)) | ||
>struct.a : Symbol(a) | ||
>struct : Symbol(struct, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 34, 5)) | ||
>a : Symbol(a) | ||
|
||
const v2: bigint = struct.b; | ||
>v2 : Symbol(v2, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 37, 5)) | ||
>struct.b : Symbol(b) | ||
>struct : Symbol(struct, Decl(mappedTypeTupleConstraintTypeParameterInNameType.ts, 34, 5)) | ||
>b : Symbol(b) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
//// [tests/cases/compiler/mappedTypeTupleConstraintTypeParameterInNameType.ts] //// | ||
|
||
=== mappedTypeTupleConstraintTypeParameterInNameType.ts === | ||
// based on https://github.com/microsoft/TypeScript/issues/55762 | ||
|
||
declare class Decoder<T> { | ||
>Decoder : Decoder<T> | ||
|
||
decode(arrayBuffer: ArrayBuffer): T; | ||
>decode : (arrayBuffer: ArrayBuffer) => T | ||
>arrayBuffer : ArrayBuffer | ||
} | ||
|
||
type ValueTypeOf<T extends Decoder<any>> = T extends Decoder<infer R> | ||
>ValueTypeOf : ValueTypeOf<T> | ||
|
||
? R | ||
: never; | ||
|
||
type StructDescriptor = ReadonlyArray< | ||
>StructDescriptor : readonly (readonly [key: string, type: Decoder<any>])[] | ||
|
||
readonly [key: string, type: Decoder<any>] | ||
>; | ||
|
||
type StructTypeFor<Descriptor extends StructDescriptor> = { | ||
>StructTypeFor : StructTypeFor<Descriptor> | ||
|
||
[K in keyof Descriptor as Descriptor[K][0]]: ValueTypeOf<Descriptor[K][1]>; | ||
}; | ||
|
||
class StructDecoder<const Descriptor extends StructDescriptor> extends Decoder< | ||
>StructDecoder : StructDecoder<Descriptor> | ||
>Decoder : Decoder<StructTypeFor<Descriptor>> | ||
|
||
StructTypeFor<Descriptor> | ||
> { | ||
constructor(descriptor: Descriptor) { | ||
>descriptor : Descriptor | ||
|
||
super(); | ||
>super() : void | ||
>super : typeof Decoder | ||
} | ||
} | ||
|
||
declare const i32Decoder: Decoder<number>; | ||
>i32Decoder : Decoder<number> | ||
|
||
declare const i64Decoder: Decoder<bigint>; | ||
>i64Decoder : Decoder<bigint> | ||
|
||
const structDecoder = new StructDecoder([ | ||
>structDecoder : StructDecoder<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>new StructDecoder([ ["a", i32Decoder], ["b", i64Decoder],]) : StructDecoder<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>StructDecoder : typeof StructDecoder | ||
>[ ["a", i32Decoder], ["b", i64Decoder],] : [["a", Decoder<number>], ["b", Decoder<bigint>]] | ||
|
||
["a", i32Decoder], | ||
>["a", i32Decoder] : ["a", Decoder<number>] | ||
>"a" : "a" | ||
>i32Decoder : Decoder<number> | ||
|
||
["b", i64Decoder], | ||
>["b", i64Decoder] : ["b", Decoder<bigint>] | ||
>"b" : "b" | ||
>i64Decoder : Decoder<bigint> | ||
|
||
]); | ||
|
||
const struct = structDecoder.decode(new ArrayBuffer(100)); | ||
>struct : StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>structDecoder.decode(new ArrayBuffer(100)) : StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>structDecoder.decode : (arrayBuffer: ArrayBuffer) => StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>structDecoder : StructDecoder<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>decode : (arrayBuffer: ArrayBuffer) => StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>new ArrayBuffer(100) : ArrayBuffer | ||
>ArrayBuffer : ArrayBufferConstructor | ||
>100 : 100 | ||
|
||
const v: number = struct.a; | ||
>v : number | ||
>struct.a : number | ||
>struct : StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>a : number | ||
|
||
const v2: bigint = struct.b; | ||
>v2 : bigint | ||
>struct.b : bigint | ||
>struct : StructTypeFor<readonly [readonly ["a", Decoder<number>], readonly ["b", Decoder<bigint>]]> | ||
>b : bigint | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we doing this only if there is a
nameType
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good question - I'm not sure 😅 it feels like a leftover or something cause at the very least the array branch should be guarded in the same way (if at all). All tests pass without this so I'll push out a change with this removed in a moment