For example, given an object:
const TypeDef1 = {
foo: {optional: true, type: 'string'},
bar: {type: 'number'}
}
which conforms to type, say
type TypeDef = {
[key: string]: {
optional?: boolean,
type: 'number' | 'string' | 'boolean'
}
}
I want to have some reusable typing Transform<T extends TypeDef>
such that
Transform<typeof TypeDef1>
gives:
{
foo?: string
bar: number
}
Is this at all possible in Typescript?
CodePudding user response:
You can use a combination of mapped and conditional types to transform the type definition to an actual type (provided we also use an as const
assertion on the definition to make sure we preserve all type info.
type TypeDef = { [key: string]: { optional?: boolean, type: 'number' | 'string' | 'boolean' } };
type TypeNameToType = {
number: number,
string: string,
boolean: boolean
}
type TypeDefToType<T extends TypeDef> = {
// The optional properties
-readonly [P in keyof T as T[P]['optional'] extends true ? P: never]?: TypeNameToType[T[P]['type']]
} & {
// The mandatory properties
-readonly [P in keyof T as T[P]['optional'] extends true ? never: P]: TypeNameToType[T[P]['type']]
}
const TypeDef1 = {
foo: {optional: true, type: 'string'},
bar: {type: 'number'}
} as const
type X = TypeDefToType<typeof TypeDef1>
// ^?
// type X = {
// foo?: string | undefined;
// } & {
// bar: number;
// }