带有受保护构造函数的TypeScrip抽象静态工厂 [英] Typescript Abstract static factory with protected constructor

查看:0
本文介绍了带有受保护构造函数的TypeScrip抽象静态工厂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作以下内容

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构造函数。

TS 4.2has add support用于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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆