typescript - 动态接口(interface)定义

标签 typescript

我有一堆“模式定义”类的抽象基类:

xyz extends BaseSchema

我想确保的是父模式类正在为模式的属性(最终为其关系)导出有意义的约束/接口(interface)。所以现在基类定义为:

export abstract class BaseSchema {
  /** The primary-key for the record */
  @property public id?: string;
  /** The last time that a given model was updated */
  @property public lastUpdated?: datetime;
  /** The datetime at which this record was first created */
  @property public createdAt?: datetime;
  /** Metadata properties of the given schema */
  public META?: Partial<ISchemaOptions>;

  public toString() {
    const obj: IDictionary = {};
    this.META.properties.map(p => {
      obj[p.property] = (this as any)[p.property];
    });
    return JSON.stringify(obj);
  }

  public toJSON() {
    return this.toString();
  }
}

示例父类可能如下所示:

export class Person extends BaseSchema {
  // prettier-ignore
  @property @length(20) public name: string;
  @property public age?: number;
  @property public gender?: "male" | "female" | "other";
  // prettier-ignore
  @property @pushKey public tags?: IDictionary<string>;

  // prettier-ignore
  @ownedBy(Person) @inverse("children") public motherId?: fk;
  // prettier-ignore
  @ownedBy(Person) @inverse("children") public fatherId?: fk;
  @hasMany(Person) public children?: fk[];

  @ownedBy(Company) public employerId?: fk;
}

现在你会注意到我正在使用 Typescript 对装饰器的支持,在这种情况下,要了解装饰器代码的主要内容是 @property 装饰器是用于显式声明模式上的属性。这会将有关模式属性的元信息标记到模式类的 META.properties 上的字典对象中。您稍后会在 toString() 方法中看到它的使用。如果您觉得理解实际的装饰器代码很重要,您可以找到它 HERE .

想要能够做的是公开一个接口(interface)定义,该定义将类型限制为架构中定义的属性和这些属性的类型。类似于以下内容:

 function foobar(person PersonProperties) { ... }

这样属性“person”将受到所需属性的约束,并且编辑器中的智能感知将提供必需的(也称为“名称”)和不需要的属性(又称为, “age”、“createdAt”等)定义为属性。

最终我想导出一个对关系执行相同操作的类型,但我很高兴现在只获取架构的属性。

最佳答案

在 typescript 2.8 中,您可以使用 conditional typesmapped types创建一个仅包含类字段而不包含方法的类型,并保留字段的必需/可选属性:

type NonMethodKeys<T> = {[P in keyof T]: T[P] extends Function ? never : P }[keyof T];  
type RemoveMethods<T> = Pick<T, NonMethodKeys<T>>; 

class Person {
  public name: string;
  public age?: number;
  public gender?: "male" | "female" | "other";
  toJson(): string { return ''}
}

let ok: RemoveMethods<Person> = {
  name : 'Snow',
  gender: 'male'
};

// Error no name
let nokNoName: RemoveMethods<Person> = {
};

let nok: RemoveMethods<Person> = {
  name : 'Snow',
  gender: 'male',
  toJson(): string { return ''}  // error unknown property
};

如果您使用的是 Typescript 2.7 或更低版本,则没有条件类型,您只能使用映射类型,例如 PickPartial。有关它们的相对优势的讨论,您可以参阅此 question

不幸的是,我们无法使用装饰器来辨别哪些字段进入了接口(interface),我们只能使用字段的类型。您可能会考虑这样一种设计,其中您的类仅包含同时也是属性的字段,或者此类字段保存在仅包含属性的特殊类中。

总是可以选择仅使用显式属性定义额外的接口(interface),但这会增加额外的维护麻烦。

关于typescript - 动态接口(interface)定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49620426/

相关文章:

typescript - 从另一个文件中导出一个 TypeScript 类,而不是在其中定义的

.net - 在列数据夹点 Angular 2 中显示列表

typescript - 枚举中的 <any> 以及 typescript 中的字符串

Angular 2 : Default checked on checkbox in ngFor

typescript 多重继承

javascript - 如何使用typescript和javascript导入npm包中的js

javascript - Angular 4 : Can't bind to 'ngForFor' since it isn't a known property of 'li'

mongodb - 'this' 的外部值被此容器与 Mongoose Schema Typescript 遮蔽

javascript - typescript : cannot read property 'long_name' of null

带有 Webpack 的 Angular - loadChildren 找不到模块