0

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?

1 Answer 1

1

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"

Playground


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` })
Sign up to request clarification or add additional context in comments.

3 Comments

As far as I can see the const is not strictly necessary here, but the [number] really helps. Thanks!
without the as const the return type would be boolean | number | string instead of true | 5 | "text"
You have a point. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.