Как я могу набрать эту функцию, которая принимает функцию mixin в качестве аргумента? - PullRequest
0 голосов
/ 20 мая 2019

У меня есть функция с именем Mixin, которая принимает один аргумент. Аргумент должен быть "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

( ссылка на игровую площадку )

Как вы видите, он принимает базовый класс и возвращает новый класс, и он отлично работает.

У меня есть утилита 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) {...}

Итак, я хотел бы выяснить, как сделать набор текста для этой Mixin утилиты в TypeScript.

Моя первая попытка следующая, которая не работает, но я думаю, что она показывает идею того, что я пытаюсь сделать:

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

( ссылка на игровую площадку )

Как вы понимаете, я пытаюсь набрать инструмент Mixin, чтобы он принимал функцию mixin, а тип возвращаемого значения для вызова Mixin должен был быть классом, который генерируется из функции mixin, и этот возвращаемый класс также должен иметь свойство .mixin, которое совпадает с типом переданной функции mixin.

Я знаю, что я делаю это неправильно. Мне не ясно, как я могу использовать здесь вывод типа.

Похоже, здесь может быть полезна новая «Вывод типа функции высшего порядка» .

Как мне добиться этой Mixin утилиты набора текста? Могу ли я сделать это без функции высшего порядка? И как это сделать с этой функцией?

1 Ответ

1 голос
/ 20 мая 2019

Это заставляет код скомпилироваться, и все типы поддержки работают, как и ожидалось, я просто не уверен, какова семантика class Bar extends CoolMixin. Я вижу, как он расширяет микширование напрямую, как если бы вы просто использовали класс миксина в качестве базового класса, не применяя его ни к чему

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

( ссылка на игровую площадку )

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...