TypeScript:带有自定义方法的工厂 - 第三步

Posted

技术标签:

【中文标题】TypeScript:带有自定义方法的工厂 - 第三步【英文标题】:TypeScript: factory with custom methods - 3rd step 【发布时间】:2022-01-23 22:22:14 【问题描述】:

我在一家工厂工作;我最终需要添加自定义方法,感谢this answer 和this answer,我们能够使其几乎按预期工作。

几乎是因为它只适用于没有任何必需参数的方法;如果我们尝试添加具有至少一个必需参数的方法,则会出现编译错误。

我尝试在 method 参数和 M 类型的声明中添加一个 rest 参数数组(见下文),但它仅在调用方法时有帮助。

(this: E & S, ...args: unknonwn[]) => unknown

type Base =  id: string 
type Factory<E> = new () => E;

function factory<E extends Base>(name: string): Factory<E>;
function factory<E extends Base, M extends Record<string, <S extends M>(this: E & S, ...args: unknown[]) => unknown>>(
  name: string, methods: M & Record<string, ((this: E & M, ...args: unknown[]) => void)>
): Factory<E & M>;
function factory<E extends Base, M extends Record<string, <S extends M>(this: E & S) => unknown>>(
  name: string, methods?: M
): Factory<E> 
  const ret = function (this: Base) 
    this.id = "0"
  ;

  Object.defineProperty(ret, "name",  value: name );

  if (methods) for (const method in methods) Object.defineProperty(ret.prototype, method,  value: methods[method] );

  return ret as unknown as Factory<E>;


const T1 = factory("T1");
const t1 = new T1();
console.log(t1, t1.id);

const T2 = factory(
  "T2",
  
    foo: function (repeat: boolean) 
      const ret = ! repeat;
      if(repeat) this.foo(ret);
      return ret;
    
  ,
);
const t2 = new T2();
console.log(t2, t2.id, t2.foo(true));

这是一个playground 进行实验。

【问题讨论】:

unknown[] 替换为any[]。见example。别担心,any 提供不安全行为时并非如此。这意味着您的函数可以返回任何参数并期望任何参数。让我知道它是否有帮助 不仅仅是“它有帮助”,我会说“它解决了”!返回类型也可以是void(至少在我到目前为止所做的测试中)。如果您介意更改答案中的评论,我可以接受 【参考方案1】:

请考虑这个代表您的用例的示例:

const foo = (fn: (a: unknown) => unknown) => fn

foo((arg: number) => arg) // error

错误: Type 'unknown' is not assignable to type 'number'

请看unknown docs:

任何东西都可以分配给unknown,但unknown 不能分配给任何东西,除了它自己和任何没有类型断言或基于控制流的缩小。

那么,如果Anything is assignable to unknown`` 为什么会出现错误?

看这个:

const bar = (a: unknown) => a
bar(42) // ok

这里好像出了点问题。不它不是。这是因为contravariance。在第一个示例中,参数a 处于逆变位置。这意味着继承的箭头指向相反的方向。请参阅this 答案和一些简单的示例和this 答案。

您需要做的就是将unknown 更改为any。不用担心,它不会给您的代码带来不安全性。其实你对methods没有任何具体的限制。

解决方案:

type Base =  id: string 
type Factory<E> = new () => E;

function factory<E extends Base>(name: string): Factory<E>;

function factory<E extends Base, M extends Record<string, <S extends M>(this: E & S, ...args: any[]) => any>>(
  name: string, methods: M & Record<string, ((this: E & M, ...args: any[]) => void)>
): Factory<E & M>;

function factory<E extends Base, M extends Record<string, <S extends M>(this: E & S, ...args: any[]) => any>>(
  name: string, methods?: M
): Factory<E> 
  const ret = function (this: Base) 
    this.id = "0"
  ;

  Object.defineProperty(ret, "name",  value: name );

  if (methods) for (const method in methods) Object.defineProperty(ret.prototype, method,  value: methods[method] );

  return ret as unknown as Factory<E>;


const T1 = factory("T1");
const t1 = new T1();
console.log(t1, t1.id);

const T2 = factory(
  "T2",
  
    foo: function (repeat: boolean) 
      const ret = !repeat;
      if (repeat) 
        this.foo(ret);
      
      return ret;
    
  ,
);
const t2 = new T2();
console.log(t2, t2.id, t2.foo(true));

Playground

【讨论】:

太棒了!再次感谢您! 您好船长!主要感谢您的帮助,我能够进行几个月来计划的重构!我正在处理sedentary;如果您想检查答案的效果,请将 v0.0.19 与 v0.0.20 进行比较,此外还有 model 方法(从 8 个重载到只有 2 个!!!现在一切都按预期工作!!!)。如果您介意将您的 github 或 npm 帐户提供给我,那么您肯定应该在贡献者列表中获得第一名! 非常感谢您的反馈。它激励。 github.com/captain-yossarian

以上是关于TypeScript:带有自定义方法的工厂 - 第三步的主要内容,如果未能解决你的问题,请参考以下文章

需要带有 npm 和 typescript 的自定义模块

找不到带有 Webpack、Typescript、自定义模块目录的模块

带有 Typescript 的 React-Native 自定义文本组件:“将 React.ReactNode 类型转换为文本类型......”

如果我在 tsconfig 中使用自定义路径,Webpack 开发服务器找不到带有 Typescript 的模块

自定义类工厂方法

如何在带有打字稿的反应功能组件中定义自定义道具?