How to copy the structure of one generic type to another generic in TypeScript?

Viewed 376

Imagine we have the following input type:

interface Input {
    name: string;
    heightCm: number;
    dob: Date;
}

I would like to write a function that can produce the following output type based on this input:

interface Output {
    name: boolean;
    heightCm: boolean;
    dob: boolean;
}

In other words, a function that copies the structure of input and returns that structure as output, setting all values as booleans.

The function signature would look something like this:

interface GenericMap<T> {
    [key: string]: T;
}

type InputToOutput<In, Out extends GenericMap<boolean>> = (input: In) => Out;

If the type of the Input property was originally a string, mark that property as true, otherwise mark as false.

E.g. the function would produce the following result based on Input above:

{
    name: true,
    heightCm: false,
    dob: false
}

But most importantly, it needs to be type-safe, such that I can receive intellisense on the resulting object.

Help greatly appreciated!

2 Answers

I think you just want a mapped type that sets the type of each property to boolean?

type GenericMap<T> = {
    [K in keyof T]: boolean
}

Which you would use like:

interface Input {
    name: string;
    heightCm: number;
    dob: Date;
}

type Output = GenericMap<Input>
// Output is
// {
//    name: boolean;
//    heightCm: boolean;
//    dob: boolean;
// }

Playground


As a (potential) improvement, you could even check for string in this type alias and return true or false, rather than boolean.

type GenericMap<T> = {
    [K in keyof T]: T[K] extends string ? true : false
}

Which would yield this type instead:

type Output = GenericMap<Input>
// {
//    name: true;
//    heightCm: false;
//    dob: false;
// }

I've managed to achieve what you want:

interface Input {
    name: string;
    heightCm: number;
    dob: Date;
}

type Out<In> = { [key in keyof In]: In[key] extends string ? true : false }
type InputToOutput<In> = (input: In) => Out<In>;
type Output = Out<Input>;

const clone: InputToOutput<Input> = input => output;

const input: Input = { name: 'foo', heightCm: 3, dob: new Date };

const output: Output = clone(input);

Which gives the following output when hovering the Output type:

const output: {
    name: true;
    heightCm: false;
    dob: false;
}
Related