I have a collection of objects with similar structure:
const a = { x : true }
const b = { x : 5 }
const c = { x : `text` }
Every objects has an x
field, but its type may be different.
All these objects are passed into a function f
as a template tuple:
function f<
T extends { x : unknown[] }
>(
...t : [...{ [i in keyof T] : T[i] }]
) {
// return t[random index].x
}
f(a, b, c)
The function f
has to return one of these x
fields, but I can't figure out how to properly specify the return type for it.
For instance, in this particular case the type should be true | 5 | "text"
.
I have tried to put T[keyof T]["x"]
as a result but got an error Type "x" cannot be used to index type 'T[keyof T]'.
How this should be done properly?
CodePudding user response:
If you want the return type to be true | 5 | "text"
you will have to use as const
to preserve the narrowed type information.
const a = { x : true } as const
const b = { x : 5 } as const
const c = { x : `text` } as const
For the function f
, you can use T
to store a tuple of the types of the x
properties.
function f<
T extends any[]
>(...t : [...{ [I in keyof T] : { x: T[I] } }]): T[number] {
return t[0]!.x
}
The return type would be T[number]
.
You now have the correct return type.
const result = f(a, b, c)
// ^? const result: true | 5 | "text"
Here is a slightly refactored version.
function f<
T extends { x: any }[]
>(...t: [...T]): T[number]["x"] {
return t[0]!.x
}
Also another version which also correctly handles object literals.
type Narrowable = string | number | boolean | symbol | object | undefined | void | null | {};
function f<
T extends { x: S }[], S extends Narrowable
>(...t: [...T]): T[number]["x"] {
return t[0]!.x
}
const result = f({ x : true }, { x : 5 }, { x : `text` })