John Reilly
John Reilly

Reputation: 6269

TypeScript: inferring data types from variadic tuple types

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;
}

Upvotes: 1

Views: 159

Answers (1)

A_blop
A_blop

Reputation: 862

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

Upvotes: 3

Related Questions