动态界面定义

时间:2018-04-03 00:08:19

标签: typescript

我有一堆"架构定义的抽象基类"类:

  

xyz延伸BaseSchema

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

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 decorator用于在架构上显式声明属性。这会将有关模式属性的元信息标记到模式类META.proproperties上的字典对象中。您将在toString()方法中稍后使用此功能。如果您觉得实际的装饰器代码很重要,您可以找到它HERE

想要能够做的是公开一个接口定义,该类型将类型约束为模式中定义的属性和类型。如下所示:

 function foobar(person PersonProperties) { ... }

以便该物业&#34; person&#34;将受到所需属性的约束,并且编辑器中的 intellisense 将提供必需的(又名,&#34;名称&#34;)和不需要的属性(又名,&#34; age& #34;,&#34; createdAt&#34;等)定义为属性。

最终,我想导出一种对关系做同样事情的类型,但我现在很高兴能够获得架构的属性。

1 个答案:

答案 0 :(得分:2)

在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

不幸的是,没有办法使用装饰器来辨别哪些字段进入界面,我们只能使用该字段的类型。您可能会考虑一种设计,其中您的类只包含属性的字段,或者此类字段保存在仅包含属性的特殊类中。

总是可以选择只显示属性的额外接口,但这会增加额外的维护麻烦。