Typescript动态创建界面

时间:2017-08-19 12:03:48

标签: javascript typescript

我使用simple-schema在对象中定义DB模式:

{
   name: 'string',
   age: 'integer',
   ...
}

是否有可能从这个对象创建一个接口或类,所以我不必两次输入所有内容?

2 个答案:

答案 0 :(得分:15)

你可以这样做,但除非你认为你可能正在改变架构,否则它可能比它的价值更麻烦。 TypeScript没有以您想要的方式推断类型的内置方式,因此您必须哄骗并哄骗它才能这样做:

首先,定义一种将文字名称'string''integer'映射到它们所代表的TypeScript类型的方法(可能分别为stringnumber):

type MapSchemaTypes = {
  string: string;
  integer: number;
  // others?
}
type MapSchema<T extends Record<string, keyof MapSchemaTypes>> = {
  [K in keyof T]: MapSchemaTypes[T[K]]
}

现在,如果您可以采用类似于您指定的类型的适当类型的架构对象,并从中获取相关类型:

const personSchema = {name: 'string', age: 'integer'}; 
type Person = MapSchema<typeof personSchema>; // ERROR

哎呀,问题是personSchema被推断为{name: string; age: string}而不是所需的{name: 'string'; age: 'integer'}。您可以使用类型注释修复它:

const personSchema: { name: 'string', age: 'integer' } = { name: 'string', age: 'integer' }; 
type Person = MapSchema<typeof personSchema>; // {name: string; age: number};

但现在感觉你在重复自己。幸运的是,有一种方法可以强制它推断出正确的类型:

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};

有效!

在行动on the Typescript Playground中查看。希望有所帮助;祝你好运!

答案 1 :(得分:1)

我认为你不能声明动态接口。但是,您可以为具有已知属性的对象创建type

您可以创建一个将字符串文字映射到实际类型的对象,例如'integer' => number,但这与问题无关。我不知道你正在使用什么框架,但以下示例适用于类似的框架:Mongoose。

users.js

export const UserSchema = mongoose.Schema({
    name: String,
    value: Number
});

export const Users = mongoose.Model('users', UserSchema);

export type User = { [K in keyof typeof UserSchema]: any } ;

用法:

import { User, Users } from './user';

Users.find({}).exec((err: Error, res: User) => { ... })

返回的结果应该与UserSchema具有相同的键,但是所有值都映射到any,因为您仍然必须将字符串文字映射到类型。