考虑这样的情况:我们从API或其他什么地方获得一些原始数据,我们希望具有ICustomer
的形状,但我们希望扩展该功能,将成员函数和其他变量作为Customer
类的一部分。
interface ICustomer {
name: string;
address: string;
}
class Customer implements ICustomer {
...
}
由此,编译器知道Customer
将(至少(具有name
和address
。有没有一种方法可以在不需要我显式键入它们的情况下定义它们?基本上我想做的是
class Customer implements ICustomer {
constructor(data: ICustomer) {
Object.assign(this, data)
}
someExtraFunctionality() { ... }
}
我不得不对每个成员定义两次,这似乎很愚蠢。
这在某种程度上可行吗?我想我可以用as any as Customer
做一些技巧,但它觉得应该有更好的方法。
不是,不是。
您可以使用类的实例类型作为接口:
type WithoutFunctions<T> = {
[K in keyof T as T[K] extends (...args: any) => any ? never : K]: T[K]
}
class Customer {
name: string;
address: string;
constructor(data: WithoutFunctions<Customer>) {
Object.assign(this, data)
}
someExtraFunctionality() {}
}
const customer = new Customer({ name: 'a', address: 'b' })
在这里,构造函数接受一个只有值属性而没有方法的对象,并将其分配给实例。不过,它需要一个笨重的实用程序类型来去掉这些功能。
类似地,您可以使用Pick
从类类型中获取您想要的属性:
type ICustomer = Pick<Customer, 'name' | 'address'>
class Customer {
name: string;
address: string;
constructor(data: ICustomer) {
Object.assign(this, data)
}
someExtraFunctionality() {}
}
const customer = new Customer({ name: 'a', address: 'b' })
这可能会给你这个错误:
Property 'name' has no initializer and is not definitely assigned in the constructor.(2564)
您可以通过将!
添加到属性中来解决这个问题,以便表示它可能是undefined
,但您应该始终将其视为string
。这使得typescript不在乎它看起来永远不会被初始化。
name!: string
不过,我不建议这样做,而且这个功能相当不安全,允许漏洞泄漏
但实际上,我认为复制属性是你最好的选择。这可能有点冗长,但Typescript会验证你做得正确,如果你没有把这些琐碎的事情做好,就会对你大喊大叫。
type ICustomer = {
name: string;
address: string;
}
class Customer implements ICustomer {
name: string;
address: string;
constructor(data: ICustomer) {
this.name = data.name
this.address = data.address
}
someExtraFunctionality() {}
}
const customer = new Customer({ name: 'a', address: 'b' })
它很简单,很清楚,非常安全,如果你弄错了任何道具的类型名称,Typescript就会亮起红色。
更新
你可以尝试使用类型而不是接口
type CustomerType = {
name: string;
address: string;
}
class Customer {
constructor(data: CustomerType) {
Object.assign(this, data)
}
}
旧答案
这可以与所有可选属性一起工作
data = Object.assign(this, data)
我通常以这种方式分配值
class Customer implements ICustomer {
constructor(data: ICustomer = {}) { //assign an empty object as a default value
for (let key in data) {
this[key] = data[key];
}
}
function someExtraFunctionality() { ... }
}
但同样,只有当所有接口属性都是可选的时,它才有效
interface ICustomer {
name?: string;
address?: string;
}
它不像Object.assign
那样简单,但它对您的案例很有用。