带有受保护构造函数的TypeScrip抽象静态工厂 [英] Typescript Abstract static factory with protected constructor
问题描述
我正在尝试制作以下内容
import { IValueObject } from "../../shared/domain/IValueObject";
import { AbstractNanoidGenerator } from "../../shared/infrastructure/AbstractNanoidGenerator";
export class CompanyID extends AbstractNanoidGenerator implements IValueObject<string, CompanyID> {
protected constructor(private companyID: string) {
super();
}
get value(): string {
return this.companyID;
}
equals(vo: CompanyID) {
return vo.companyID === this.companyID;
}
}
export abstract class AbstractNanoidGenerator {
static generate<T extends AbstractNanoidGenerator>(this: new (ID: string) => T): T {
return new this(nanoid());
}
static fromString<T extends AbstractNanoidGenerator>(this: new (ID: string) => T, id: string) {
return new this(id);
}
}
export interface IValueObject<T, FinalClass> {
readonly value: T;
equals(_vo: FinalClass): boolean;
}
它可以工作,但他们要求我将子类的构造函数声明为公共。我不会这么做的。
The 'this' context of type 'typeof CompanyID' is not assignable to method's 'this' of type 'new (ID: string) => CompanyID'. Cannot assign a 'protected' constructor type to a 'public' constructor type.ts(2684)
有什么想法吗?
Here is the link to the playground
问候
推荐答案
问题
protected
是访问修饰符。您不能将其放在类型或接口上,因为它们是public contracts。因此,我们将无法在类型上指定受保护的属性,该类型将在定义函数的参数时使用,包括this
。
在我们的例子中,当我们说this: new (ID: string) => T
时,我们说的是this
传递的参数(或者从技术上讲,这个函数被调用的上下文对象)应该是new (ID: string) => T
类型。它仅代表公共属性。:(
截至2021年11月(TS 4.5),仍然没有任何方法来谈论接口的protected
属性(参见this comment),和/或指定类的protected
构造函数。
abstract
构造签名。因此,希望在未来我们也能看到对受保护构造函数的支持。
黑客攻击
我们了解到,您不能强制传递的this
参数具有受保护的构造函数。下一个最佳选择是什么,比any
更好吗?:)
假设我们很好地假设,如果它是AbstractNanoidGenerator
的子类,它总是会有一个受保护的构造函数,该构造函数接受(ID: string)
并返回其自身的一个实例。(相同的子类。)
因此,我们的任务只是将this
参数强制为AbstractNanoidGenerator
的子类,并将返回类型正确确定为该子类型的实例。
类与实例对象
首先,让我们回顾一下";类和";实例对象之间的区别。
当您说let id: CompanyID
时,id
的类型是CompanyID类的实例。
this
不应该是this: CompanyID
,这意味着我们需要一个实例!我们希望传递/使用实际的CompanyID
类本身,它的类型为typeof CompanyID
(参见this answer)。:)
我们应该如何说我们需要类本身?
在TypeScrip中,有两种主要方式可以将文字设置为类别。在我们的例子中,它将用于强制this
参数。
类类型版本1:使用new
暗示某事是";Class";的最常见方法是说,如果您对其调用new
,它将生成一个";实例&。这促使(this type of solutions)使用以下实用程序函数。
export interface Type<T> extends Function {
new (...args: any[]): T;
}
// --- Or, similarly ---
export type Constructor<VALUE_T = any> = new (...args: any[]) => VALUE_T;
类类型版本2:使用prototype
上述版本的问题在于,它不适用于带有受保护构造函数的类,如CompanyID
。此类类没有可由new
拾取的公共构造函数,并且将在那里失败。
这就是我们在第二种方式中依赖名为prototype
的属性的原因。
export type ClassDefinitionFor<T> = { prototype: T };
(所以,let theClass:ClassDefinitionFor<CompanyID> = CompanyID
有效!)
要扭转这一局面...
type InstanceOfClass<T> = T extends { prototype: infer R } ? R : never;
现在参数应该是什么?
根据上面的两个实用程序函数,我编写了以下内容。
请注意,使用as unknown as
技巧,我们告诉TS要考虑这一点,因为this
是一个扩展AbstractNanoidGenerator
的Class对象,它有一个构造函数来生成一个对象,其类型位于该类的prototype
上。
type ClassDefinitionFor<T> = { prototype: T };
type InstanceOfClass<T> = T extends { prototype: infer R } ? R : never;
export abstract class AbstractNanoidGenerator {
static generate<
INSTANCE_T extends AbstractNanoidGenerator,
CLASS_T extends ClassDefinitionFor<INSTANCE_T>,
CTR_T extends new (ID: string) => InstanceOfClass<CLASS_T>,
>(this: CLASS_T): InstanceOfClass<CLASS_T> {
return new (this as unknown as CTR_T)(Math.random().toString());
}
}
(Full TS Playground with extra tests)
这篇关于带有受保护构造函数的TypeScrip抽象静态工厂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!