我有一个抽象基类,我想要toJSON()
和toString()
的通用实现。对于toString()
,我可以这样实现:
public toString() {
const obj: IDictionary = {};
this.META.properties.map(p => {
obj[p.property] = (this as any)[p.property];
});
return JSON.stringify(obj);
}
然后,假设一个 Person 对象扩展我的抽象基类,如下所示:
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;
}
注意:装饰器代码是非令人兴奋的,我将其排除在外,因为我认为它会使事情变得不必要......如果人们对超级感兴趣,可以在here找到所有装饰者
然后我在某处看到了一个输入签名:
public set<T>(path: string, person: T) { ... }
传递公共方法完全没问题,并且不要求我传递toString
方法。但是,当我使用名为toJSON()
的方法完全时,它会给我错误:
[TS] 类型'{name:string;年龄:数量; ''不能赋值给'Person'类型的参数。 类型'{name:string;'中缺少属性'toJSON';年龄:数量; }“
有谁理解为什么会这样?
答案 0 :(得分:1)
Typescript有一个结构类型系统,其中类型兼容性由结构兼容性决定。这意味着具有Person
类的所有字段和方法的对象文字与类型Person
的参数兼容。
如果我们的类只有字段和toString
方法,则任何具有必填字段的对象文字都是兼容的,因为toString
方法隐式存在于所有对象上。一旦添加其他方法,编译器就会开始抱怨对象文字没有额外的方法。您可以通过定义额外的方法使对象文字再次兼容,但对象文字仍然不是类的实例
class Person{
name: string;
toJson() {
return this.name;
}
}
let literal: Person = {
name: '',
toJson() { return '';}
}
console.log(literal instanceof Person) //false!
您应该定义一个构造函数,该构造函数将类的字段作为参数并将它们分配给类的字段。然后使用new
创建类的实例:
type NonMethodKeys<T> = ({[P in keyof T]: T[P] extends Function ? never : P } & { [x: string]: never })[keyof T];
type RemoveMethods<T> = Pick<T, NonMethodKeys<T>>;
class Person {
constructor(data: RemoveMethods<Person>) {
Object.assign(this, data)
}
name: string;
toJson() {
return this.name;
}
}
let aPerson: Person = new Person({
name: '',
});
console.log(aPerson instanceof Person) //true
或者,如果您希望set方法只占用person的字段,则可以使用RemoveMethods<Person>
作为set
的类型参数,但我不确定set
的作用和如果它关心参数是Person
的实际实例,或者它只需要Person
db.set<RemoveMethods<Person>>('url', {
name:'',
age:42
}) // should compile but might not work, hard to say without more info on set