1

Variadic tuple types allow us to provide the caller of a function, a tuple return type driven by the tuple supplied to the function like so:

function makeArrayAsConstItems<T extends readonly any[]>(...arr: [...T]): {[K in keyof T]: {item: T[K]} } {
    return arr.map(item => ({ item })) as any;
}

const arrayAsConstItems = makeArrayAsConstItems('cfgh', 1, new Date());

// this is the type
const arrayAsConstItems: [{
    item: string;
}, {
    item: number;
}, {
    item: Date;
}]

Now consider we want to do two more things:

  1. Narrow the items that can be supplied in each array element to be a type of Data<TypeOfData>
  2. Extract the type we're going to be returning from Data<TypeOfData>; so use TypeOfData essentially
interface Data<TypeOfData = unknown> {
    data: TypeOfData;
}

function makeArrayAsConstItemsForDataTypesOnly<T extends readonly Data[]>(...arr: [...T]): {[K in keyof T]: {item: T[K]} } {
    return arr.map(item => ({ item })) as any;
}

const arrayAsConstItemsForDataTypesOnly = makeArrayAsConstItemsForDataTypesOnly(
    { data: 'cfgh' }, 
    { data: 1 }, 
    { data: new Date() }
)

What would we need to do for arrayAsConstItemsForDataTypesOnly to have the same type as arrayAsConstItems?

At present the type is:

const arrayAsConstItemsForDataTypesOnly: [{
    item: {
        data: string;
    };
}, {
    item: {
        data: number;
    };
}, {
    item: {
        data: Date;
    };
}]

And we desire it to be:

const arrayAsConstItemsForDataTypesOnly: [{
    item: string;
}, {
    item: number;
}, {
    item: Date;
}]

This is @A_blop's suggestion:

function makeArrayAsConstItemsForDataTypesOnly<T extends readonly Data[]>(...arr: [...T]): {[K in keyof T]: {item: Extract<T[K], Data>["data"]} } {
    return arr.map(item => ({ item })) as any;
}
3
  • 2
    Have you tried {[K in keyof T]: {item: Extract<T[K], Data>["data"]} } ? Commented Jan 3, 2021 at 16:31
  • that works! Do you want to add it as an answer and I'll accept? Commented Jan 3, 2021 at 16:47
  • 1
    sure, thanks. Have added an answer below. Commented Jan 3, 2021 at 17:28

1 Answer 1

3

We can do that by utilizing lookup types in combination with the Extract utility type:

function fn<T extends readonly Data[]>(...arr: [...T]):
    { [K in keyof T]: { item: Extract<T[K], Data>["data"] } } {/* impl */}
const arrayAsConstItemsForDataTypesOnly = fn(
    { data: 'cfgh' },
    { data: 1 },
    { data: new Date() }
)
/*
[{
    item: string;
}, {
    item: number;
}, {
    item: Date;
}]
 */

As TypeScript cannot resolve T[K] further, we need to first convince it again, that the resulting type has a data property accessible via lookup operator. This is done by Extract.

Playground

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks so much for your answer @A_blop! Just what I needed; in case you're curious, the motivation was working on this: github.com/tannerlinsley/react-query/pull/…

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.