Home > other >  TypeScript Extend a dynamically generated type by an additional property
TypeScript Extend a dynamically generated type by an additional property

Time:01-10

Based on this question, I have implemented my custom types:

export enum Category {
  car = 'car',
  truck = 'truck',
}

export interface Property {
  readonly name: string,
  readonly label: string,
  readonly type: 'number' | 'string',
}


export interface CategoryDefinition {
  readonly category: Category,
  readonly label: string,
  readonly properties: readonly Property[]
}

type TypeMap = {
  string: string;
  number: number;
};

type FromDefinition<D extends CategoryDefinition> = {
  [T in D['properties'][number] as T['name']]: TypeMap[T['type']];
};

For this (roughly):

const car = {
      category: Category.car,
      label: 'Car',
      properties: [
      {
        name: 'weight',
        label: 'Weight',
        type: 'number',
      }
    ],
} as const satisfies CategoryDefinition; 

export type Car = FromDefinition<typeof car>;

correctly creates

type Car = {
    weight: number;
}

Let's say, I would have to expand this type, so that it includes the category, looking like that:

type Car = {
    category: 'car';
    weight: number;
}

The closest I could come up with, is this:

export type CategoryExtension= {
  category: Category
};

type FromDefinition<D extends CategoryDefinition> = {
  [T in D['properties'][number] as T['name']]: TypeMap[T['type']];
} & CategoryExtension; // <----

export type Car = FromDefinition<typeof car>;

which kind of results in what I want, but looks odd:

type Car = {
    height: number;
} & CategoryExtension // <---- 

How can I create the following?:

type Car = {
    height: number;
    category: 'car' // or Category
} 

CodePudding user response:

Traditionally this is done by making TS infer the result and a homomorphic mapped type:

type FromDefinition<D extends CategoryDefinition> = ({
  [T in D['properties'][number] as T['name']]: TypeMap[T['type']];
} & CategoryExtension) extends infer O ? { [K in keyof O]: O[K] } : never;
//                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  • Related