我有一个使用通用接口创建类的函数。实例的属性由该泛型的参数设置,如下所示:
const ClassFactory = <T>() => {
class MyClass {
constructor(data: T) {
for (const key in data) {
if (!data.hasOwnProperty(key)) { continue; }
(this as any)[key] = data[key];
}
}
// other methods can be here
}
return MyClass;
}
const GeneratedClass = ClassFactory<{ id: number }>();
const myInstance = new GeneratedClass({ id: 1 });
console.log((myInstance as any).id); // logs 1
这按预期运行,但是有两个问题
myInstance
的输入没有T键-我希望myInstance.id
是一个数字this as any
才能通过给定的数据分配值为了解决第一个问题,我尝试了从其他帖子中看到的各种事情,包括
class MyClass implements T
,但它们都导致相同的错误:A class can only implement an object type or intersection of object types with statically known members.ts(2422)
。
我知道为什么会发生这种情况,但是由于定义类时知道T
,有没有办法使它起作用?
如果我的数据位于public data: T
属性中,则正确键入myInstance.data.id
。所以我的问题是,可以通过跳过.data
部分来做到这一点吗?
预先感谢
答案 0 :(得分:4)
受到乔纳斯·威尔姆斯(Jonas Wilms)的评论的启发,即使该类具有方法/静态函数,我也可以通过返回以下内容使它起作用
return MyClass as (new (data: T) => T & InstanceType<typeof MyClass>) & typeof MyClass;
像这样,以下所有内容均按预期方式键入并运行
const myInstance = new GeneratedClass({ id: 1 });
console.log(myInstance.id, GeneratedClass.someStatic(), myInstance.someMethod());
但是,如果在类方法中使用new MyClass()
,则无法正常工作。
一种可行的解决方法是创建一个静态方法,该方法返回具有正确类型的实例
// inside the class
public static build(data: T): T & InstanceType<typeof MyClass> {
return new MyClass(data) as T & InstanceType<typeof MyClass>;
}
那么以下是预期的
const myInstance = GeneratedClass.build({ id: 1 });
console.log(myInstance.id, GeneratedClass.someStatic(), myInstance.someMethod());
完整的示例
const ClassFactory = <T>() => {
class MyClass {
constructor(data: T) {
for (const key in data) {
if (!data.hasOwnProperty(key)) { continue; }
(this as any)[key] = data[key];
}
}
public static build(data: T): T & InstanceType<typeof MyClass> {
return new MyClass(data) as T & InstanceType<typeof MyClass>;
}
public static someStatic() {
return 2;
}
public someMethod() {
return 3;
}
}
return MyClass as (new (data: T) => T & InstanceType<typeof MyClass>) & typeof MyClass;
}
const GeneratedClass = ClassFactory<{ id: number }>();
const myInstance = new GeneratedClass({ id: 1 });
console.log(myInstance.id, GeneratedClass.someStatic(), myInstance.someMethod());
const myBuiltInstance = GeneratedClass.build({ id: 1 });
console.log(myBuiltInstance.id, GeneratedClass.someStatic(), myBuiltInstance.someMethod());
答案 1 :(得分:0)
我只是遇到了这个问题,并且能够通过简单地抛弃new
并创建一个通用的静态方法Class.create()
来解决问题,并将其实例化并转换为正确的类型。
interface MyGenericInterface<T = any> {
getTheThing(): T
}
class MyClass implements MyGenericInterface {
// Make constructor private to enforce contract
private constructor(private thing: any) {}
public getTheThing(): any {
return this.thing
}
static create<TypeOfThing = any>(thing: TypeOfThing) {
return new MyClass(thing) as MyGenericInterface<TypeOfThing>
}
}
const stringyInstanceWrong = new MyClass('thing') // Error
const stringyInstance = MyClass.create('thing')
const stringThing: string = stringyInstance.getTheThing()
const numberyInstance = MyClass.create(123)
const numberyThingWrong: string = numberyInstance.getTheThing() // Error
const numberyThing: number = numberyInstance.getTheThing() // Works!