-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Mapped tuples types iterates over all properties #27995
Comments
This is because, despite mapped types not behaving as such, |
I wouldnt mind a fix for this as well. I've been encountering similar issues, but here's how I've been working around it for anyone else with this problem: (my code generalized below with Boxes) interface Box<T> {
value: T;
}
type UnBox<T extends Box<unknown>> = T extends Box<infer U> ? U : never;
type UnBoxTuple<T extends Box<unknown>[]> = {
[P in keyof T]: UnBox<T[P]>;
}; Above code complains that type UnBoxTuple<T extends Box<unknown>[]> = {
[P in keyof T]: T[P] extends T[number] ? UnBox<T[P]> : never;
}; Trying to apply the same to @lemoinem's example didn't work at first. It seems like the mapped tuple special-case only applies to generics (try the following in ts 3.1.3 to see what I mean): type Foo = ["a", "b"];
type GenericTupleIdentity<T extends unknown[]> = { [K in keyof T]: T[K] };
type FooIdentity = { [K in keyof Foo]: Foo[K] }
type GenericTupleIdentityTest = GenericTupleIdentity<Foo> // ["a", "b"]
type FooIdentityTest = FooIdentity // a Foo-like object no longer recognised as a tuple Not sure if this inconsistency is intended or not. Abstracting-out type Bazify<B, F extends (keyof B)[]> = {
[K in keyof F]: F[K] extends F[number] ? B[F[K]] : never;
}; Which can then be called with It's worth noting that |
The issue here is that we only map to tuple and array types when we instantiate a generic homomorphic mapped type for a tuple or array (see #26063). We should probably also do it for homomorphic mapped types with a |
Just stumbled on what I assume is the same issue:
I would expect n2 to behave like n2b. |
This also means that these constructs don't work:
The second one should definitely work. This means that it's impossible to map tuples to other tuples with a method that requires the value type of the input tuple to be known to extend something (e.g. number as above) |
@lemoinem You can get your current example to narrow correctly and avoid the behavior @weswigham mentioned with a couple of helper conditional types. Note: this comes with some not so great drawbacks.. You lose all array prototype methods and properties, which for my current use case--messing with React hooks--that tradeoff isn't the worst thing in the world. export type ArrayKeys = keyof any[];
export type Indices<T> = Exclude<keyof T, ArrayKeys>;
type Foo = ['a', 'b'];
interface Bar {
a: string;
b: number;
}
type Baz = { [K in Indices<Foo>]: Bar[Foo[K]] }; // type is { "0": string, "1": number } The way this type ends up formatting isn't the greatest, but it ultimately maps to some of your desired outcomes. I am having issues around some constructs I thought would work, similar to @phiresky 's examples. I will get some better examples together and document those, but for now just wanted to provide a bit of a workaround. |
@ksaldana1 Thanks, nice idea! type WorkingBaz = { [K in Exclude<keyof Foo, keyof any[]>]: Bar[Foo[K]]; } & { length: Foo['length']; } & any[] It avoids the drawback of losing the Array prototype's methods (only their typing info, which are already much less useful on heterogeneous tuples...). But I'd still expect this to work out of the box and produce the correct |
I'm also seeing the same bug. I was about to log an issue, but I saw this one in the related issues page. For reference, the code I'm experiencing this bug with: type Refinement<A, B extends A> = (a: A) => a is B
// we get the error `Bs[k] does not satisfy the constraint 'A'. Type A[][k] is not assignable to type A`
type OrBroken = <A, Bs extends A[]>(...preds: { [k in keyof Bs]: Refinement<A, Bs[k]> }) => Refinement<A, Bs[number]>
// same (well similar, [A, A, A][k] is not assignable vs A[][k] not assignable) error
type OrBroken1 = <A, Bs extends [A, A, A]>(...preds: { [k in keyof Bs]: Refinement<A, Bs[k]> }) => Refinement<A, Bs[number]>
// if we don't map over the type we don't receive the error
type OrWorks = <A, Bs extends A[]>(...preds: { [k in keyof Bs]: Refinement<A, Bs[0]> }) => Refinement<A, Bs[number]>
type OrWorks1 = <A, Bs extends A[]>(...preds: { [k in keyof Bs]: Refinement<A, Bs[number]> }) => Refinement<A, Bs[number]> Expected behavior: Playground Link: |
You could just filter for number properties: type Foo = ['a', 'b'];
interface Bar
{
a: string;
b: number;
}
// Inverse of Exclude<>
type FilterOnly<T, N> = T extends N ? T : never;
type Baz = {
[K in FilterOnly<keyof Foo, number>]: Bar[Foo[K]];
}; |
I think we have enough and various workarounds and comments from the TS team identifying the root issue. This will prevent spamming everyone in the thread. Thank you very much. |
Hey there, I'm running into this issue, except I'm using a generic tuple type instead of a statically declared tuple. type PropertyType = "string" | "number" | "boolean"
type PropertyValueTypeMap = {
string: string,
number: number,
boolean: boolean
}
type Args<A extends Array<PropertyType>> = {
// ERROR: Type 'A[I]' cannot be used to index type 'PropertyValueTypeMap'.
[I in keyof A]: PropertyValueTypeMap[A[I]]
}
// RESULT: type A = [string, number]
type A = Args<["string", "number"]> @augustobmoura's |
Considering that this works: type Args<A extends Array<PropertyType>> = {
0: PropertyValueTypeMap[A[0]]
1: PropertyValueTypeMap[A[1]]
2: PropertyValueTypeMap[A[2]]
} ... I would have expected this to work, but it doesn't: type Args<A extends Array<PropertyType>> = {
[K in keyof A]: K extends number ? PropertyValueTypeMap[A[K]] : never
} Also tried this... type Args<A extends Array<PropertyType>> = {
[K in keyof A]: K extends keyof Array<any> ? never : PropertyValueTypeMap[A[K]]
} |
@ccorcos I just had the same problem, solved it like this type PropertyType = "string" | "number" | "boolean"
type PropertyValueTypeMap = {
string: string,
number: number,
boolean: boolean
}
type Args<A extends Array<PropertyType>> = {
[I in keyof A]: A[I] extends PropertyType ? PropertyValueTypeMap[A[I]] : never;
}
// RESULT: type A = [string, number]
type A = Args<["string", "number"]> |
I've frequently been bitten by this. It's come up enough for me that I now usually avoid using mapped types with tuples and instead resort to arcane recursive types to iterate over them. I'm not a fan of doing this, but it's proven to be a much more reliable means of tuple transformations. 😢 @ahejlsberg from your comment it's not clear how complex this is to solve. Is the fix simple? If the team is looking to get help from the community on this one, it may help to have a brief outline of the necessary work if possible (e.g. relevant areas of the compiler, any necessary prework, gotchas, etc). 🙂 |
Just as an aside... Flow has an entirely separate utility type for mapping over arrays/tuples. Given TypeScript has gone with a syntatic approach to mapped types thus far, I can't help but wonder: Should we have separate, dedicated syntax for array/tuple mapping? 🤔 An example might be simply using square brackets instead of curlies, e.g. I just can't shake the feeling that trying to specialise the behaviour of the existing mapped types for arrays/tuples may create more problems than it solves. This very issue is a consequence of trying to do it. There are also cases like "I want to map over a tuple type from an object perspective" that don't have clear answers to me, but that's an entirely separate discussion. Anyway, I'm not sure if dedicated syntax has already been considered and rejected. I'm more just throwing it out there in case it hasn't. 🙂 |
Further that @tao-cumplido would it be possible to extend that so that the length of infered from a keyof? eg: type Things = 'thing-a' | 'thing-b';
type Test = Args<string, Things>; // [string, string]
type Test2 = Args<string | number, Things>; // [string | number, string | number ]
type Test2 = Args<string, Things | 'thing-3'>; // [string string, string]
declare const test:Args<string, Things> = ['a', 'b']; |
I've been trying to come up with a way to map over a type interface Box<V = unknown> {
value: V
}
function box<V>(value: V) {
return { value }
}
type Unbox<B extends Box> = B['value']
type NumericKey<i> = Extract<i, number | "0" | "1" | "2" | "3" | "4"> // ... etc
type UnboxAll<T extends Box[]> = {
[i in keyof T]: i extends NumericKey<i> ? Unbox<T[i]> : never
}
declare function unboxArray<T extends Box[]>(boxesArray: T): UnboxAll<T>
declare function unboxTuple<T extends Box[]>(...boxesTuple: T): UnboxAll<T>
const arrayResult = unboxArray([box(3), box('foo')]) // (string | number)[]
const tupleResult = unboxTuple(box(3), box('foo')) // [number, string] So far, I haven't been able to find a variant that works for any numeric key, without the hardcoding ( As stated before, I am specifically trying to avoid testing |
Actually, the following totally works for my purposes: interface Box<V = unknown> {
value: V
}
function box<V>(value: V) {
return { value }
}
type Unbox<B extends Box> = B['value']
type UnboxAll<T extends Box[]> = {
[i in keyof T]: Unbox<Extract<T[i], T[number]>> // !!!
}
declare function unboxArray<T extends Box[]>(boxesArray: T): UnboxAll<T>
declare function unboxTuple<T extends Box[]>(...boxesTuple: T): UnboxAll<T>
const arrayResult = unboxArray([box(3), box('foo')]) // (string | number)[]
const tupleResult = unboxTuple(box(3), box('foo')) // [number, string] I just needed to test |
This is still something that irks me daily, because I have a lot of code that maps over array types, whether or not they are tuples. When mapping over For example: type Box<T = unknown> = { value: T }
type Values<X extends Box[]> = {
[i in keyof X]: Extract<X[i], Box>['value'] // Extract is needed here
}
type MyArray = Values<{value: number}[]> // number[]
type MyTuple = Values<[{value: number}, {value: string}]> // [number, string] It's worth noting that the behavior shown in While I have no knowledge of the internals here, my guess is that the actual mapping behavior could be changed rather easily; the problem is getting It would be really nice if this code would just work: type Values<X extends Box[]> = {
[i in keyof X]: X[i]['value']
} The question is, presumably, on what basis can Here is perhaps a novel idea, not sure if it is useful, but I wonder if it would be harder or easier to modify the compiler so that this code works for mapping arrays and tuples: type Values<X extends Box[]> = {
[i in number]: X[i]['value']
} As before, the intent would be for this to work on pure arrays and pure tuples X, and do something sensible on non-pure types. It seems like there is no more problem of making sure |
Alternatively, just to throw it out there, there could be a new keyword |
Trying to generalize this (at least the first part): export type ArrayKeys = keyof unknown[];
export type TupleKeys<T extends unknown[]> = Exclude<keyof T, ArrayKeys>;
type PickTuple<T extends Record<string, any>, TupleT extends Array<keyof T>> = {
[K in TupleKeys<TupleT>]:T[TupleT[K]];
}; gives But this seems to workaround that: export type ArrayKeys = keyof unknown[];
export type TupleKeys<T extends unknown[]> = Exclude<keyof T, ArrayKeys>;
type PickTuple<T extends Record<string, any>, TupleT extends Array<keyof T>> = {
[K in TupleKeys<TupleT>]:
TupleT[K] extends keyof T ? T[TupleT[K]] : never;
}; giving: type PickTuple<T extends Record<string, any>, TupleT extends Array<keyof T>> = {
[K in TupleKeys<TupleT>]:
TupleT[K] extends keyof T ? T[TupleT[K]] : never & { length: TupleT['length']; } & unknown[];
}; EDIT: I just saw this comment which provides something similar but manages to keep the tuple (since it filters for number properties like mentioned here): type Bazify<B, F extends (keyof B)[]> = {
[K in keyof F]: F[K] extends F[number] ? B[F[K]] : never;
}; |
I just discovered this potential workaround as of TypeScript 4.1: type BoxElements<Tuple extends readonly unknown[]> = {
[K in keyof Tuple]: K extends `${number}` ? Box<Tuple[K]> : Tuple[K]
}; I haven't tested it extensively, but seems to work for my current use case! 🙂 |
And maybe also where |
At the very top of the thread, @weswigham commented:
Did this behavior change at some point? Per this playground, |
@thw0rted I don't believe they have numeric-literal keys, but rather numeric-string-literal keys. In your case, I believe Playground example (hover over |
OK, looks like what was confusing was that now the language service emits "keyof [string,string]" instead of the whole explicit litany of Array member functions, unless you remove one of them as in your example. Check the Asserts tab of this Bug Workbench -- it shows
You can also open that Workbench and change around TS versions. There was a change at some point. The output above is from 4.2.3, but in 4.1.5, and 4.0.5, they both have the long spelled-out list (minus |
I came here because I needed some way of using an object's entries, while not caring at all it is a tuple or a record. const someValueInEntriesOf = <TSubject>(subject: TSubject): TSubject[keyof TSubject] => {...} This is where I realized why I could not: I did a little investigation and summarized things below: Subjects// Types
type tuple = [number, number[], string, boolean[][]]
type array = string[]
type record = {zero: 0, one: 1, two: 2, three: 3}
// Subjects
const tupleSubject: tuple = [0, [1], 'foo', [[true]]]
const arraySubject: array = ['zero', 'one', 'two', 'three']
const recordSubject: record = {zero: 0, one: 1, two: 2, three: 3} Fetch subject key typeconsole.log(Object.keys(tupleSubject)) // logs <["0", "1", "2", "3"]>, as expected. BTW Object.keys returns <string[]> instead of <(keyof typeof arraySubject)[]>.
type tupleKeyofKeys = keyof tuple // resolves as <number | keyof tuple>, why does it include <number>? And why does <keyof tuple> is made of itself anyway?
type tupleRealKeys = Exclude<keyof tuple, keyof []> // resolves as <"0" | "1" | "2" | "3">, fixes the issue.
console.log(Object.keys(arraySubject)) // logs <["0", "1", "2", "3"]>, as expected. BTW Object.keys returns <string[]> instead of <(keyof typeof arraySubject)[]>.
type arrayKeyofKeys = keyof array // resolves as <number | keyof array>, why does it include <keyof array>, is <number> not sufficient?
type arrayRealKeys = number // using <Exclude<keyof array, keyof []>> resolves as <never> :/
console.log(Object.keys(recordSubject)) // logs <["zero", "one", "two", "three"]>, as expected. BTW Object.keys returns string[] instead of <(keyof typeof recordSubject)[]>.
type recordKeyofKeys = keyof record // resolves as <keyof record>, which further resolves as <"zero" | "one" | "two" | "three">, good.
type recordRealKeys = Exclude<keyof record, keyof []> // resolves as <"zero" | "one" | "two" | "three">, good too. Fetch one of the subject's items typeFetch one of the TUPLE's items typetype tupleKeyAccess_inRange = tuple[1] // resolves as <number[]>, good
type tupleKeyAccess_outOfRange = tuple[4] // throws <Tuple type 'tuple' of length '4' has no element at index '4'.ts(2493)>, good.
type tupleKeyAccess_generic = tuple[number] // resolves as <string | number | number[] | boolean[][]>, but is strictly incorrect. 4 is a number and cannot index the tuple type, see tupleKeyAccess_outOfRange.
type tupleKeyAccess_keyofKeys = tuple[tupleKeyofKeys] /* resolves as (sorry for your eyes):
<string | number | number[] | boolean[][] | (() => IterableIterator<string | number | number[] | boolean[][]>) | (() => {
copyWithin: boolean;
entries: boolean;
fill: boolean;
find: boolean;
findIndex: boolean;
keys: boolean;
values: boolean;
}) | ... 30 more ... | (<A, D extends number = 1>(this: A, depth?: D | undefined) => FlatArray<...>[])>.
This is not expected behaviour, seems like tupleKeyofKeys includes Object.getPrototypeOf(tupleSubject) keys. */
type tupleKeyAccess_realKeys = tuple[tupleRealKeys] // resolves as <string | number | number[] | boolean[][]>, good. Fetch one of the ARRAY's items typetype arrayKeyAccess_inRange = array[1] // resolves as <string>, as using strict mode, I expected <string | undefined>.
type arrayKeyAccess_outOfRange = array[4] // resolves as <string>, as using strict mode, I expected <string | undefined>.
type arrayKeyAccess_generic = array[number] // resolves as <string>, as using strict mode, I expected <string | undefined>.
type arrayKeyAccess_keyofKeys = array[arrayKeyofKeys] /* resolves as (sorry for your eyes):
<string | number | (() => IterableIterator<string>) | (() => {
copyWithin: boolean;
entries: boolean;
fill: boolean;
find: boolean;
findIndex: boolean;
keys: boolean;
values: boolean;
}) | ... 30 more ... | (<A, D extends number = 1>(this: A, depth?: D | undefined) => FlatArray<...>[])>
This is not expected behaviour, seems like arrayKeyofKeys includes Object.getPrototypeOf(arraySubject) keys. */
type arrayKeyAccess_realKeys = array[arrayRealKeys] // resolves as string, good Fetch one of the RECORD's items typetype recordKeyAccess_inRange = record['one'] // resolves as <1>, good.
type recordKeyAccess_outOfRange = record['four'] // throws <Property 'four' does not exist on type 'record'.ts(2339)>, good.
type recordKeyAccess_generic = record[string] // throws <Type 'record' has no matching index signature for type 'string'.ts(2537)>, very good!!
type recordKeyAccess_keyofKeys = record[recordKeyofKeys] // resolves as <0 | 1 | 2 | 3>. Still good there.
type recordKeyAccess_realKeys = record[recordRealKeys] // resolves as <0 | 1 | 2 | 3>, good good. What went wrong (summary)Object access/indexing is not consistant among containers in TypeScript (mostly for historic reason I suppose, some operators/features were missing at the time), specificaly among arrays, tuples and records:
In the meantime, records:
Additionaly, those are problematic too:
What I use now until the TypeScript lang contributors fix the thing(s)type IsTuple<T> = T extends unknown[] ? true : false // I found no way to tell apart an array from a tuple. This line must be improved as you will see later why.
type TupleFixedKeyof<T> = IsTuple<T> extends true ? Exclude<keyof T, keyof []> : keyof T // THE fix
type tupleKeysIExpected = TupleFixedKeyof<tuple> // resolves as <"0" | "1" | "2" | "3">, fixes tuples.
type arrayKeysIDidNotExpected = TupleFixedKeyof<array> // resolves as never... Use TupleFixedKeyof wisely (only when not using arrays, only tuples and records) until IsTuple exists natively in TypeScript some way.
type recordKeysIExpected = TupleFixedKeyof<record> // resolves as <keyof record> which further resolves as <"zero" | "one" | "two" | "three">, behaviour is correct for records. |
I've been digging into this a little bit with the debugger and for what it is worth - I think this could be fixed if only a mapped type like this could be "resolved" (to not call it as instantiation) in If only we could recognize there that the constraint for the iteration refers to a tuple type and that mapping would be homomorphic then the assigned I've tried to accomplish this - but I'm not sure how to best "resolve" this mapped type, I've looked through what |
If any part of this question has to do with generic The part where the type you're iterating over is non-generic remains, though. |
The first given repro in this thread doesn't use a generic mapped type - so I would say that this issue is still sound despite the improvements made in the #48837. Luckily there is also a fix for this issue in #48433 but it still awaits the review. |
To expand on @Kyasaki's workaround a bit: I found it is possible to distinguish between tuples and arrays like this: type GetKeys<T> = T extends unknown[]
? T extends [] // special case empty tuple => no keys
? never
: "0" extends keyof T // any tuple with at least one element
? Exclude<keyof T, keyof []>
: number // other array
: keyof T; // not an array
type TupleKeys = GetKeys<[1, 2, 3]>; // "0" | "1" | "2"
type EmptyTupleKeys = GetKeys<[]>; // never
type ArrayKeys = GetKeys<string[]>; // number
type RecordKeys = GetKeys<{ x: 1; y: 2; z: 3 }>; // "x" | "y" | "z"
type GetValues<T> = T[GetKeys<T> & keyof T];
type TupleValues = GetValues<[1, 2, 3]>; // 1 | 2 | 3
type EmptyTupleValue = GetValues<[]>; // never
type ArrayValues = GetValues<string[]>; // string
type RecordValues = GetValues<{ x: 1; y: 2; z: 3 }>; // 1 | 2 | 3 Not sure this is bulletproof, but at least solves my use case nicely. 😉 |
I'm running into a similar issue: type MapTuple <T extends any[]> = {
[K in keyof T]: {
value: T[K]
}
}
type Foo<T extends (...args: any[]) => any> = MapTuple<Parameters<T>>
const f = <T extends (...args: any[]) => any>(a: Foo<T>): Parameters<T> => {
return a.map(v => v.value)
}
// 'map' inferred as { value: Parameters<T>[never]; } and not callable. Is there a work around in this case? 👀 |
@Deadalusmask I feel like your issue is likely related to another issue |
Is the following issue related to this one as well? It shows that inlining a type mapper can lead to iteration over all array properties and methods. type InnerMapper<Attributes> = {
[Index in keyof Attributes]: Attributes[Index];
};
//--- The inlined mapper MapperA2 yields different results when the attributes are handed in via an interface
interface AttributeWrapper {
attributes: string[];
}
type MapperA1<Wrapper extends AttributeWrapper> = InnerMapper<Wrapper['attributes']>;
type MapperA2<Wrapper extends AttributeWrapper> = {
[Index in keyof Wrapper['attributes']]: Wrapper['attributes'][Index];
};
type ResultA1 = MapperA1<{ attributes: ['type', 'id'] }>;
// ^? type ResultA1 = ["type", "id"]
type ResultA2 = MapperA2<{ attributes: ['type', 'id'] }>;
// ^? type ResultA2 = { [x: number]: "type" | "id"; 0: "type"; 1: "id"; length: 2; toString: () => string; ...
//--- Results of both mappers are identical when attributes are handed in directly
type MapperB1<Attr extends string[]> = InnerMapper<Attr>;
type MapperB2<Attr extends string[]> = {
[Index in keyof Attr]: Attr[Index];
};
type ResultB1 = MapperB1<['type', 'id']>;
// ^? type ResultB1 = ["type", "id"]
type ResultB2 = MapperB2<['type', 'id']>;
// ^? type ResultB1 = ["type", "id"] Compiler Options{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"target": "ES2017",
"jsx": "react",
"module": "ESNext",
"moduleResolution": "node"
}
} Playground Link: Provided |
New Workaround for TuplesRan into this too, but I found a more concise and simpler work around using recursive types with the bonus of tail-recursion optimization: Fix: type TupleKeysToUnion<T extends readonly unknown[], Acc = never> = T extends readonly [infer U, ...infer TRest]
? TupleKeysToUnion<TRest, Acc | U>
: Acc;
export type MapOfTupleKeys<T extends readonly unknown[]> = { [K in Extract<TupleKeysToUnion<T>, PropertyKey>]: K }; Example that turns a tuple into a map of keys with itself: type Example = ['a', 'b', 'c'];
type KeysAsUnion = TupleKeysToUnion<Example>;
//result: "a" | "b" | "c"
type MappedResult = MapOfTupleKeys<Example>;
// type MappedResult = {
// a: "a";
// b: "b";
// c: "c";
// } |
Very much letters
|
I've come up with a more concise and straightforward method that should be able to address all possible scenarios.
|
TypeScript Version: 3.2.0-dev.20181019
Search Terms: mapped tuples reify
Code
Expected behavior: Baz should be [string, number]
Actual behavior: Type '["a", "b"][K]' cannot be used to index type 'Bar'.
Playground Link: https://www.typescriptlang.org/play/index.html#src=type%20Foo%20%3D%20%5B'a'%2C%20'b'%5D%3B%0D%0Ainterface%20Bar%0D%0A%7B%0D%0A%09a%3A%20string%3B%0D%0A%09b%3A%20number%3B%0D%0A%7D%0D%0A%0D%0Atype%20Baz%20%3D%20%7B%20%5BK%20in%20keyof%20Foo%5D%3A%20Bar%5BFoo%5BK%5D%5D%3B%20%7D%3B%20%2F%2F%20Expected%20Baz%20to%20be%20%5Bstring%2C%20number%5D%0D%0A%0D%0Atype%20WorkingBaz%20%3D%20%7B%20%5BK%20in%20Exclude%3Ckeyof%20Foo%2C%20keyof%20any%5B%5D%3E%5D%3A%20Foo%5BK%5D%20extends%20keyof%20Bar%20%3F%20Bar%5BFoo%5BK%5D%5D%20%3A%20never%3B%20%7D%20%26%20%7B%20length%3A%20Foo%5B'length'%5D%3B%20%7D%20%26%20any%5B%5D%3B
Related Issues: #25947
Given the Mapped tuple types feature (#25947). I'd expect the code above to work cleanly.
However, I still need to do:
To have an equivalent type. As far as I understand, the "K" in a mapped type on a tuple should iterate only on numeric keys of this tuple. Therefore, Foo[K] should always be a valid key for Bar...
The text was updated successfully, but these errors were encountered: