打字稿:在静态方法中使用 Child Class

Typescript: Work with Child Class in static method

在 TypeScript(我使用 Playground,版本 4.13)中,当我从 class 继承时,this 在 [=48 的 static 方法中=] class 好像是指继承 class:

class Parent{
    static ID = 0
    public id: number

    static create(){
        return new this(this.ID)
    }

    constructor(id: number){
        this.id = id
    }
}

class Child extends Parent{
    static ID = 1
}

let child = Child.create()
console.log(child.id)  // 1

但是,当我想根据 child class:

的类型定义一些行为时,我遇到了问题
class Parent{
    static create(data: object){
        let instance = new this
        for (let [key, value] of Object.entries(data)){
            this[key] = value
        }
        return instance
    }
}

class Child extends Parent{
    id: number | null = null
}

let child = Child.create({id: 1})
console.log(child.id)

这给了我

Element implicitly has an 'any' type because expression of type 'string | number | symbol' can't be used to index type 'typeof Parent'. No index signature with a parameter of type 'string' was found on type 'typeof Parent'.

我试图通过将 key 类型转换为 child class:

的键来解决这个问题
class Parent{
    static create(data: object){
        let instance = new this
        for (let [key, value] of Object.entries(data)){
            this[key as keyof this] = value
        }
        return instance
    }
}

class Child extends Parent{
    id: number | null = null
}

let child = Child.create({id: 1})
console.log(child.id)

但这是被禁止的。我得到

A 'this' type is available only in a non-static member of a class or interface

此外,我得到(在所有情况下)

Property 'id' does not exist on type 'Parent'.

我该如何解决我的问题 - 从 object 动态填充 child class 的属性(我从我的 real-world场景)?

您可以通过指定与 class 本身相对应的 this 参数来完成此操作。在静态方法中,this 指的是 class 本身。

static create<T extends Parent>(this: new (...args: any[]) => T, data: object) {...} 

这里发生的事情是我们说 this 类型,它将引用包含该方法的对象,在这种情况下,create 是什么 class 对象在调用时,可以 return 实例类型的子类型 class。这是通过类型参数 T 实现的,class 对象将具有 return 一个 T 的构造签名,从而捕获任何派生的实例类型class.

这是完整的工作代码:

class Parent {
    static create<T extends Parent>(this: new (...args: any[]) => T, data: object) {
        let instance = new this;
        for (let [key, value] of Object.entries(data)) {
            instance[key as keyof T] = value;
        }
        return instance;
    }
}

class Child extends Parent {
    id: number | null = null;
}

let child = Child.create({ id: 1 });
console.log(child.id);

Playground Link

由于我们通过 T 捕获了派生实例类型,我们可以进一步改进 create 方法,通过调整创建来提高类型安全性,如下所示:

static create<T extends Parent>(this: new (...args: any[]) => T, data: Partial<T>) {...}

这会阻止我们在提供智能感知的同时将任意属性分配给 create 方法创建的对象。

完整代码:

class Parent {
    static create<T extends Parent>(this: new (...args: any[]) => T, data: Partial<T>) {
        let instance = new this;
        for (let [key, value] of Object.entries(data)) {
            const k = key as keyof T;
            instance[k] = value as T[typeof k];
        }
        return instance;
    }
}

class Child extends Parent {
    id: number | null = null;
}

let child = Child.create({ id: 1 });
console.log(child.id);

Playground Link