简体   繁体   English

打字稿动态创建界面

[英]Typescript dynamically create interface

I use simple-schema to define DB schemas in an object:我使用简单模式在对象中定义数据库模式:

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

Is it somehow possible to create an interface or class from this object, so I don't have to type everything twice?是否有可能从这个对象创建一个接口或类,所以我不必两次输入所有内容?

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: TypeScript 没有以您想要的方式推断类型的内置方法,因此您必须哄骗它这样做:


First, define a way of mapping the literal names 'string' and 'integer' to the TypeScript types they represent (presumably string and number respectively):首先,定义一种将文字名称'string''integer'映射到它们所代表的 TypeScript 类型(大概分别是stringnumber )的方法:

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}糟糕,问题是personSchema被推断为{name: string; age: string} {name: string; age: string} instead of the desired {name: 'string'; age: 'integer'} {name: string; age: string}而不是所需的{name: 'string'; age: 'integer'} {name: 'string'; age: 'integer'} . {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: 2020-06 更新:在更新的 TS 版本中,您可以使用const断言来获得相同的结果:

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

That works!那个有效!


See it in action on the Typescript Playground . 在 Typescript Playground 上查看它的实际效果 Hope that helps;希望有所帮助; good luck!祝你好运!

I don't think you can declare dynamic interfaces.我认为您不能声明动态接口。 However, you can create a type for objects with known properties.但是,您可以为具有已知属性的对象创建类型

You can create an object that maps string literals to actual types, eg 'integer' => number , but that is not relevant to the question.您可以创建一个将字符串文字映射到实际类型的对象,例如'integer' => number ,但这与问题无关。 I don't know what framework you're using but the following example works for a similar looking framework: Mongoose.我不知道您使用的是什么框架,但以下示例适用于外观相似的框架:Mongoose。

users.js用户.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 } ;

usage:用法:

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

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

The returned result should have the same keys as UserSchema , but all values are mapped to any as you would still have to map string literals to types.返回的结果应该与UserSchema具有相同的键,但所有值都映射到 any ,因为您仍然需要将字符串文字映射到类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM