从TypeScript开始,我尝试使用keyof
在类上定义动态属性:
type UserType = {
id: number,
name: string,
}
class Domain<T> {
_data: T;
[K in keyof T]: T[K]; // Does not build:
// (1) A computed property name must be of type
// 'string', 'number', 'symbol', or 'any'
// (2) Cannot find name 'keyof'
constructor(data: T) {
this._data = data;
Object.keys(data).forEach((key) => Object.defineProperty(this, key, { get: () => this._data[key] }));
}
}
const joeData: UserType = {
id: 1,
name: 'Joe',
}
const joe = new Domain(joeData); // type: Domain<UserType>
console.log(joe.id);
我确实使用与()相同的语法编写了这段代码:
type Foo<T> = {
[K in keyof T]: T[K];
}
有关如何解决此问题的任何提示?
答案 0 :(得分:3)
映射的类型不能在接口或类中使用。它们只能在类型别名中使用。
如果涉及泛型,则接口和类继承不能与映射类型很好地混合。 Typescript希望预先知道基类/接口的形状,因此我们可以编写如下内容:
interface Foo extends Pick<UserType, keyof UserType> {
}
我们不能写
interface Foo<T> extends Pick<T, keyof T> { //An interface can only extend an object type or intersection of object types with statically known members
}
这消除了使用类接口合并欺骗编译器的任何可能性。
唯一的解决方法是分别定义类,并使用自定义构造函数签名。定制构造函数签名可以具有通用类型参数,并且可以在返回的实例类型中使用它。我们可以将返回的类型设为Domain
类的实例,与T
相交:
type UserType = {
id: number,
name: string,
}
class _Domain<T> {
_data: T;
constructor(data: T) {
this._data = data;
Object.keys(data).forEach((key) => Object.defineProperty(this, key, { get: () => this._data[key] }));
}
}
const Domain = _Domain as ({
new <T>(data: T): _Domain<T> & T
})
const joeData: UserType = {
id: 1,
name: 'Joe',
}
const joe = new Domain(joeData); // type: _Domain<UserType> & UserType
console.log(joe.id);
答案 1 :(得分:0)
我认为现在有办法动态指定这样的类属性。它也不能在interface
中使用。我用implements Foo<T>
尝试了您的班级变体,并收到以下错误消息:
类只能实现对象类型或具有静态已知成员的对象类型的交集。
这使我相信Typescript是专门为避免动态指定的类属性而设计的。
您可以使用非类的构造函数来执行此操作。您可以将Foo
类型用作使用getter构造普通对象的函数的返回类型。或者,如果您只需要对象的只读版本,则可以使用Object.freeze(data)
。