TypeScript Version: 3.9.0
Search Terms:
mapped type tuple object keys
union of keys of all objects in a tuple
Code
type A = [{
b: 'b'
}, {
a: 'a',
}];
type B = {
[i in Extract<keyof A, number>]: keyof A[i];
}[Extract<keyof A, number>];
function b(b: B) {
b // is type never, should be "a" | "b"
}
My thinking:
{ [i in Extract<keyof A, number>]: keyof A[i]; }
// should map to
["a", "b"]
// so indexing that resulting tuple
[Extract<keyof A, number>]
// should return
"a" | "b"
Can be worked around by hard coding expected tuple lengths ...
type Indices = 0 | 1 | 2 ...;
type B = {
[i in Indices]: keyof A[i];
}[Indices];
Expected behavior:
I would like to extract a union of all the keys found in any of the objects in a tuple. It should return "a" | "b", not never.
Actual behavior:
type B is never.
See #13298; this is not supported and will not be.
Is that what I鈥檓 trying to do? I鈥檓 not looking to get a tuple from a union. I鈥檓 trying to get a union from a tuple. I also showed how it鈥檚 possible with a workaround. Am I misunderstanding the issue you linked?
type A = [{
b: 'b'
}, {
a: 'a',
}];
type Keys1<T extends { [n: number]: unknown }> = T[number];
type Keys2<T> = T extends T ? keyof T : never;
type Keys<T extends { [n: number]: unknown }> = Keys2<Keys1<T>>;
// "b" | "a"
type B = Keys<A>;
Thanks for the quick turnaround on a solution!
I鈥檓 curious, why is the T extends T required?
Is there a reason why my original example doesn鈥檛 work or is it a bug?
Thanks again! You rock.
You need to distribute the union out and do the key extraction on each part, rather than taking keyof on the union as a whole
Yeah that makes sense. I thought I was doing that by taking keyof each item in the tuple and union of the results. Guess not? Feel free to close if you consider this resolved. Thanks again.
It's tricky to predict when things will distribute like you expect and when they won't. The behavior in the OP is as intended, I believe.
Most helpful comment