【问题标题】:TypeScript: factory with custom methodsTypeScript:具有自定义方法的工厂
【发布时间】:2022-01-16 19:48:29
【问题描述】:

我在工厂工作,最终需要添加自定义方法。我无法使用 overloads 添加自定义方法,但是当我开始添加自定义方法时,一切都无法正常工作。 :(

我能够成功涵盖没有提供自定义方法的情况以及自定义方法参数类型错误的情况。

type Base = { id: string }
type Methods<T> = { [key: string]: (this: T) => unknown };

function factory<T extends Base>(method: (this: T) => void): (new () => T);
function factory<
    T extends Base & M,
    M extends Methods<T>
>(method: (this: T) => void, methods: M): (new () => T);
function factory<
    T extends Base & M,
    M extends Methods<T>
>(method: (this: T) => void, methods?: M): (new () => T) {
    return null as unknown as (new () => T);
}

// Ok: T = Base
factory(function() {
    this.id = this.a();
});

// Ok: 0 is not valid value for argument methods (we don't care about T in this case)
factory(function() {
    this.id = this.a();
}, 0);

如果我将有效值传递给自定义方法参数,则没有任何效果! playground是一个检查问题详情的强大工具。

// Nothing working as desired
factory(function() {
    this.id = this.a();
}, {
    a: function(): string {
        this.a();
        this.b();
        return "0";
    }
});

如果我们鼠标悬停factory函数名上,我们可以看到它的类型是:

function factory<Base & Methods<unknown>, {
    a: (this: Base & Methods<unknown>) => string;
}>

所以我们可以这么说

type T = Base & Methods<unknown>
type M = { a: (this: Base & Methods<unknown>) => string; };

这就是问题所在:既然T某种东西,为什么M 被解析为Methods&lt;unknown&gt; 而不是Methods&lt;something&gt;

还有很多其他问题(b方法不被认为是错误,a方法被认为返回unknown而不是string作为对象的a属性传递为methods论点等)但似乎它们都是错误的M类型解析的副作用。

我强烈怀疑所有这些问题的根本原因是TM 之间以及T 和自身之间的循环依赖关系,但我不能没有因为

  1. T 需要依赖 M 为其提供自定义方法;
  2. M 需要依赖 T 来推断自定义方法的隐式 this 参数的类型。

知道如何实现这个吗?

【问题讨论】:

  • TypeScript 从左到右解析泛型。在这种情况下,我认为最好对其进行一些重构。首先,您需要推断所有方法,然后推断您的工厂函数。见here。所以我只是替换了参数。让我知道它是否有帮助
  • 谢谢@captain-yossarian,差不多!仍然不起作用的是自定义方法在其中不可见:playground中的详细信息

标签: typescript


【解决方案1】:

TypeScript 从左到右解析/推断泛型。

在这种情况下,您需要先推断 Methods,然后再推断 method 本身。

我只是替换了第一个和第二个参数:

type Base = { id: string }

const factory = <
  T extends Base,
  Methods extends Record<string, <Self extends Methods>(this: T & Self) => unknown>,
 >(methods: Methods, method: (this: T & Methods) => void) => {
  return null as any
}

factory({
  a: function () {
    this.id = this.a(); // Ok: string
    const b = this.b(); // Ok: number
    const c = this.c(); // Ok: method does not exists
    return "0";
  },
  b: function () {
    this.id = this.a(); // Ok: string
    const b = this.b(); // Ok: number
    const c = this.c(); // Ok: method does not exists
    return 0;
  },
}, function () {
  this.id = this.a(); // Ok: string
  const b = this.b(); // Ok: number
  const c = this.c(); // Ok: method does not exists
});

Playgroud

为了推断每种方法的this,我使用了额外的通用参数&lt;Self extends Methods&gt;(this: T &amp; Self) =&gt; unknown

如果您想知道为什么我添加了Self 而不是只使用Methods,请查看this 答案。

附:我有一条经验法则:如果您对参数推理有疑问 - 添加一个更通用的。通常它会有所帮助:D

【讨论】:

  • 哇!它似乎实际上是我正在寻找的。那是我的一个项目,我在我的矛头时代工作;现在我正忙于我的实际工作:稍后我将使用这个解决方案,我会给你更多的反馈。非常感谢!
  • @DanieleRicci 看起来像这样 :D 甚至没有注意到
  • @DanieleRicci 我有一条经验法则:如果您对参数推断有疑问 - 添加一个更通用的。通常它会有所帮助:D
  • @DanieleRicci 非常感谢!
  • 完成!如果你能检查my new question...谢谢
猜你喜欢
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多