You can do this, but it might be more trouble than it's worth unless you think you might be changing the schema. TypeScript doesn't have built-in ways of inferring types in a way that you want, so you have to coax and cajole it to do so:
First, define a way of mapping the literal names 'string'
and 'integer'
to the TypeScript types they represent (presumably string
and number
respectively):
type MapSchemaTypes = {
string: string;
integer: number;
// others?
}
type MapSchema<T extends Record<string, keyof MapSchemaTypes>> = {
-readonly [K in keyof T]: MapSchemaTypes[T[K]]
}
Now if you can take an appropriately typed schema object like the one you specified, and get the associated type from it:
const personSchema = {name: 'string', age: 'integer'};
type Person = MapSchema<typeof personSchema>; // ERROR
Oops, the problem is that personSchema
is being inferred as {name: string; age: string}
instead of the desired {name: 'string'; age: 'integer'}
. You can fix that with a type annotation:
const personSchema: { name: 'string', age: 'integer' } = { name: 'string', age: 'integer' };
type Person = MapSchema<typeof personSchema>; // {name: string; age: number};
But now it feels like you're repeating yourself. Luckily there is a way to force it to infer the proper type:
function asSchema<T extends Record<string, keyof MapSchemaTypes>>(t: T): T {
return t;
}
const personSchema = asSchema({ name: 'string', age: 'integer' }); // right type now
type Person = MapSchema<typeof personSchema>; // {name: string; age: number};
UPDATE 2020-06: in more recent TS versions you can use a const
assertion to get the same result:
const personSchema = { name: 'string', age: 'integer' } as const;
type Person = MapSchema<typeof personSchema>;
That works!
See it in action on the Typescript Playground. Hope that helps; good luck!
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…