我有一堆“模式定义”类的抽象基类:
xyz
extendsBaseSchema
我想确保的是父模式类正在为模式的属性(最终为其关系)导出有意义的约束/接口(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 types和 mapped 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 或更低版本,则没有条件类型,您只能使用映射类型,例如 Pick
或 Partial
。有关它们的相对优势的讨论,您可以参阅此 question
不幸的是,我们无法使用装饰器来辨别哪些字段进入了接口(interface),我们只能使用字段的类型。您可能会考虑这样一种设计,其中您的类仅包含同时也是属性的字段,或者此类字段保存在仅包含属性的特殊类中。
总是可以选择仅使用显式属性定义额外的接口(interface),但这会增加额外的维护麻烦。
关于typescript - 动态接口(interface)定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49620426/