Search code examples
typescriptinterfacemapped-types

Typescript generic that generates an interface of builder function types


Given a Typescript interface:

interface Product {
  url: URL;
  available: boolean;
  price: number;
}

I want to create a generic type which given an interface can output a builder type:

interface ProductSteps {
  url: (data: unknown) => URL;
  available: (data: unknown) => boolean;
  price: (data: unknown) => number;
}

I have tried using the Record utility type to generate the Builder:

type Steps<T> = Record<keyof T, (data: unknown) => T[keyof T]>
type ProductSteps = Steps<Product>;

This works OK but it results in allowing any of the value types of Product as the return types:

type ProductSteps {
  url: (data: unknown) => URL | boolean | number;
  available: (data: unknown) => URL | boolean | number;
  price: (data: unknown) => URL | boolean | number;
}

Any ideas on how to restrict the return type of the builder functions to their corresponding type in Product?


Solution

  • We want to create a mapped type based on two generic values: an ObjectType like Product and a DataType representing the data that we are mapping. For each key of the object type ([P in keyof ObjectType]), the corresponding value type is a function that takes the data and maps it to the object value for that key ((data: DataType) => ObjectType[P]).

    type Steps<ObjectType, DataType> = {
      [P in keyof ObjectType]: (data: DataType) => ObjectType[P]
    };
    

    Calling this with your Product and an arbitrary SomeDataInterface like this:

    type Test = Steps<Product, SomeDataInterface>;
    

    resolves to:

    type Test = {
        url: (data: SomeDataInterface) => URL;
        available: (data: SomeDataInterface) => boolean;
        price: (data: SomeDataInterface) => number;
    }
    

    Typescript Playground Link