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;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^