网站搜索

typescript - 定义一个静态异步 create 函数,其参数与构造函数相同


我已经写了许多类,需要重构以使用异步构造函数。

例如:

class ClassA {
    constructor(a: number, b: string, c: string) {
        //...
    }
    //...
}

我添加了一个异步创建方法:

class ClassA {

    // added async 
    static async create(...args: ConstructorParameters<typeof ClassA>){
        await loadSameResources()
        return new this(...args)
    }

    constructor(a: number, b: string, c: string) {
        //...
    }
}

它运行良好,用户只需要将 new ClassA(...) 改为 (await ClassA.create(...)),这很简单,并且保持必要的修改很小。

我的问题是如何在创建函数中避免写 'ClassA',像下面这样,这样我可以在每个类中复制这个函数(我不能修改类继承)

    // added async 
    static async create(...args: ConstructorParameters<typeof this>){ // error here
        await loadSameResources()
        return new this(...args)
    }

答案#1

你可以做类似的事情,比如让构造函数调用一个私有初始化函数,这样提取参数就容易得多。

class ClassA {

    // added async 
    static async create(...args: Parameters<typeof this.prototype.init>){
        await loadSameResources(() => {})
        return new this(...args)
    }

    constructor(...args: Parameters<typeof this.init>) {
        this.init(...args)
    }

    private init(a: number, b: string, c: string) {}
}

让你所有类的构造函数调用私有的 init 函数,使得构造函数和 create 函数都可以很容易地从这个 init 函数推断出各自的参数类型。

现在你可以将 create 和 constructor 复制/粘贴到你的每个类中,无需修改。

正如其他用户建议的,如果你不需要公开构造函数,你可能也想将其设为私有的。

答案#2

在更改你的目标语义时,你可以非常简单地创建一个通用函数来构建你的实例:

import { Constructor } from 'type-fest'

async function create<TInstance>(Class: Constructor<TInstance>, ...args: ConstructorParameters<typeof Class>) {
    await loadSameResources()
    return new Class(...args)
}

const a = await create(ClassA, 0, 'b', 'c')
//    ^? const a: ClassA

游乐场链接

优势:

  • create 函数只在一个地方定义(更容易维护)
  • 无需更改您当前的课程

另一种可能性,它可以保留你的目标语义((await ClassA.create(...))),但稍微冗长一些,是使用类混入(Class Mixin):

function WithCreateMixin<TInstance extends object>(BaseClass: Constructor<TInstance>) {
    return class BaseClassWithCreate extends BaseClass {
        static async create(...args: ConstructorParameters<typeof BaseClass>) {
            await loadSameResources()
            return new BaseClass(...args)
        }
    }
}

class ClassA extends WithCreateMixin(class ClassA0 {
    constructor(a: number, b: string, c: string) {
        //...
    }
    //...
}) { }

const a = await ClassA.create(0, 'b', 'c')
//    ^? const a: ClassA0

游乐场链接

create 静态函数仍然只在一个地方定义,但现在你需要包装你现有的类(使用 class ClassX extends WithCreateMixin(/* 原始类 */) { })。