【问题标题】:How can I type this function that accepts a mixin function as argument?如何键入这个接受 mixin 函数作为参数的函数?
【发布时间】:2019-10-07 04:04:02
【问题描述】:

我有一个名为Mixin 的函数,它接受一个参数。参数应该是“类工厂混合”。

例如,假设我有这个类工厂 mixin 函数:

type Constructor<T = any, A extends any[] = any[]> = new (...a: A) => T

const CoolMixin = <T extends Constructor>(Base: T) => {
  return class CoolMixin extends Base {
    coolProp = 42
  }
}

const CoolFoo = CoolMixin(class Foo {
    foo = 'asdf'
})

const c = new CoolFoo()

// it works:
c.foo
c.coolProp

(playground link)

如您所见,它接受一个基类并返回一个新类,并且工作正常。

我有一个 Mixin 实用程序,它采用 mixin 函数,并为其提供了很酷的功能,例如支持 hasInstance、缓存基类的重复应用程序以及其他功能。

在纯 JavaScript 中,我可以这样使用它:

// Mixin returns an application of the Mixin function (a class) with
// a default base class applied (Object by default):
const CoolMixin = Mixin((Base) => {
  return class CoolMixin extends Base {
    coolProp = 42
  }
})


// Here, CoolMixin is `class CoolMixin extends Object {...}`,
// so we can use it like a regular class:
let CoolFoo = class Foo extends CoolMixin {
    foo = 'asdf'
}


// Mixin returns that class with a static `.mixin` property containing
// the original mixin function, so we can also use it as a mixin:
CoolFoo = CoolMixin.mixin(class Foo {
    foo = 'asdf'
})

// either of the two versions will work the same:
const c = new CoolFoo()
c.foo
c.coolProp

因此,我的实用程序的便利性(除了缓存、hasInstance 等功能之外)是可以使用但最方便。这里还有两个例子:

// suppose One and Two are mixins created with my Mixin utility.

// Use regular extension:
class Foo extends One {...}
class Bar extends Two {...}

// or compose them together:
class Baz extends One.mixin(Two) {...}

所以,我想弄清楚如何在 TypeScript 中为这个 Mixin 实用程序进行输入。

我的第一次尝试是以下,它不起作用,但我认为它显示了我正在尝试做的事情的想法:

type Constructor<T = any, A extends any[] = any[]> = new (...a: A) => T

type MixinFunction = <TSub, TSuper>(base: Constructor<TSuper>) =>
  Constructor<TSub & TSuper>

declare function Mixin<TSub, TSuper, T extends MixinFunction>(mixinFn: T):
  Constructor<TSub & TSuper> & {mixin: T}

// Then using it like so:

const CoolMixinFunction = <T extends Constructor>(Base: T) => {
  return class CoolMixin extends Base {
    coolProp = 42
  }
}

const CoolMixin = Mixin(CoolMixinFunction)

const CoolFoo = CoolMixin.mixin(class Foo {
    foo = 'asdf'
}

const c = new CoolFoo()
c.foo
c.coolProp

const CoolBar = class Bar extends CoolMixin {
    bar = 'asdf'
})

const b = new CoolBar()
b.bar
b.coolProp

(playground link)

正如您可能推断的那样,我正在尝试键入Mixin 工具,以便它接受一个mixin 函数,并且Mixin 调用的返回类型应该是一个从mixin 函数生成的类,并且返回的类还应该有一个 .mixin 属性,它与传入的 mixin 函数的类型相同。

我知道我做错了。我不清楚如何在这里使用类型推断。

似乎新的"Higher order function type inference" 功能在这里可能有用。

我怎样才能实现这种Mixin 实用程序输入?如果没有更高阶的功能,我可以做到吗?以及如何使用该功能?

【问题讨论】:

  • 我的猜测是高阶支持将在 TS3.5 中得到更好的支持,它将合并 this pull request,因为您在这里专门查看构造函数类型。但这只是目前的猜测。

标签: typescript typescript-typings typescript-generics


【解决方案1】:

这使得代码编译并且所有的道具类型都按预期工作,我只是不确定class Bar extends CoolMixin 的语义是什么。我看到它直接扩展 mix-in 的方式就像您只是将 mixin 类用作基类,而没有将它应用于任何东西

type Constructor<T = any, A extends any[] = any[]> = new (...a: A) => T

// The function is not generic on two type parameters:
// it is a generic type on TSub as that is fixed during definition
// and a generic function on TSuper as that is defined during the mix-in call
// although TSub does not much matter so we can erase it
type MixinFunction = <TSuper>(base: Constructor<TSuper>) => Constructor<TSuper>

declare function Mixin<T extends MixinFunction>(mixinFn: T): ReturnType<T> & { mixin: T }

// Then using it like so:
// The {} in the extends is critical to allow ReturnType above to get an insatnce of mixin as if applied to {}.
const CoolMixinFunction = <T extends Constructor<{}>>(Base: T) => {
    return class CoolMixin extends Base {
        coolProp = 42
    }
}

const CoolMixin = Mixin(CoolMixinFunction)

const CoolFoo = CoolMixin.mixin(class Foo {
    foo = 'asdf'
})

const c = new CoolFoo()
c.foo
c.coolProp

const CoolBar = class Bar extends CoolMixin {
    bar = 'asdf'
}

const b = new CoolBar()
b.bar
b.coolProp

(playground link)

【讨论】:

  • “我看到它直接扩展混入的方式就像您只是将混入类用作基类,而没有将其应用于任何东西”。是的,Mixin 工具返回一个 mixin 的应用程序,如果在内部调用它,那么它返回 mixin 生成的类,但它使用默认基类(Object 作为默认基类)调用它。然后在返回之前将.mixin 属性粘贴到生成的类上。
  • mixin函数不是返回Constructor&lt;TSub &amp; TSuper&gt;,因为它返回了生成的子类的构造函数吗?
  • @trusktr 以及TSub 实际上是TSuper 的子类型,所以TSub &amp; TSuper 可以简化为TSub。这里的主要思想是我们并不真正关心TSub 是什么,我们只关心mixin 函数返回的内容至少是TSuper
  • 似乎缺少一些东西,但在实践中我发现它有效。有没有办法让它推断返回类型constructor&lt;TSub&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-22
  • 2020-06-28
  • 2023-01-30
  • 2014-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多