1

在 TypeScript(我使用Playground,版本 4.13)中,当我从一个类继承时,父类this的方法内部static似乎是指继承类:

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

但是,当我想根据子类的类型定义一些行为时,我遇到了问题:

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)

这给了我

元素隐式具有“任何”类型,因为表达式类型为“字符串 | 号码 | 符号'不能用于索引类型'typeof Parent'。在“typeof Parent”类型上找不到带有“string”类型参数的索引签名。

我试图通过类型转换key作为子类的键来解决这个问题:

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)

但这是被禁止的。我明白了

“this”类型仅在类或接口的非静态成员中可用

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

“父”类型上不存在属性“id”。

如何解决我的问题 - 从对象(我在现实世界场景中从 API 接收)动态填充子类的属性?

4

1 回答 1

2

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

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

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

这是完整的工作代码:

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);

游乐场链接

由于我们已经通过 捕获派生实例类型T,我们可以create通过调整 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);

游乐场链接

于 2021-01-14T10:24:56.523 回答