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(/* 原始类 */) { })。
