如何键入接受mixin函数作为参数的函数?

时间:2019-05-20 19:08:23

标签: typescript typescript-typings typescript-generics typescript-higher-order-types

我有一个名为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实用程序?没有该更高阶功能,我可以这样做吗?以及如何使用该功能?

1 个答案:

答案 0 :(得分:1)

这使得代码可以编译,并且所有prop类型都可以按预期工作,我只是不确定class Bar extends CoolMixin的语义是什么。我看到它直接扩展混入的方式就像您只是将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