Skip to content

Commit

Permalink
improvement: make 'compose' helper infer types from the old mixin ret…
Browse files Browse the repository at this point in the history
…urn value
  • Loading branch information
thetutlage committed Jun 28, 2022
1 parent f0a591b commit 8007301
Showing 1 changed file with 75 additions and 15 deletions.
90 changes: 75 additions & 15 deletions src/Helpers/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@

type Constructor = new (...args: any[]) => any

/**
* Converting unions to intersection
*/
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any
? R
: never
/* eslint-disable no-redeclare */

/**
* Normalizes constructor to work with mixins. There is an open bug for mixins
Expand All @@ -26,18 +21,83 @@ export type NormalizeConstructor<T extends Constructor> = {
new (...args: any[]): InstanceType<T>
} & Omit<T, 'constructor'>

export interface UnaryFunction<T, R> {
(source: T): R
}

/**
* Compose a class by applying mixins to it.
* The code is inspired by https://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/, its
* just that I have added the support for static types too.
*/
export const compose = <
SuperClass extends Constructor,
M extends ((superclass: SuperClass) => Constructor)[]
>(
superclass: SuperClass,
...mixins: M
): SuperClass & UnionToIntersection<ReturnType<M[number]>> => {
return mixins.reduce((c, mixin) => mixin(c), superclass) as SuperClass &
UnionToIntersection<ReturnType<M[number]>>
export function compose<T extends Constructor, A>(superclass: T, mixin: UnaryFunction<T, A>): A
export function compose<T extends Constructor, A, B>(
superclass: T,
mixin: UnaryFunction<T, A>,
mixinB: UnaryFunction<A, B>
): B
export function compose<T extends Constructor, A, B, C>(
superclass: T,
mixin: UnaryFunction<T, A>,
mixinB: UnaryFunction<A, B>,
mixinC: UnaryFunction<B, C>
): C
export function compose<T extends Constructor, A, B, C, D>(
superclass: T,
mixin: UnaryFunction<T, A>,
mixinB: UnaryFunction<A, B>,
mixinC: UnaryFunction<B, C>,
mixinD: UnaryFunction<C, D>
): D
export function compose<T extends Constructor, A, B, C, D, E>(
superclass: T,
mixin: UnaryFunction<T, A>,
mixinB: UnaryFunction<A, B>,
mixinC: UnaryFunction<B, C>,
mixinD: UnaryFunction<C, D>,
mixinE: UnaryFunction<D, E>
): E
export function compose<T extends Constructor, A, B, C, D, E, F>(
superclass: T,
mixin: UnaryFunction<T, A>,
mixinB: UnaryFunction<A, B>,
mixinC: UnaryFunction<B, C>,
mixinD: UnaryFunction<C, D>,
mixinF: UnaryFunction<E, F>
): F
export function compose<T extends Constructor, A, B, C, D, E, F, G>(
superclass: T,
mixin: UnaryFunction<T, A>,
mixinB: UnaryFunction<A, B>,
mixinC: UnaryFunction<B, C>,
mixinD: UnaryFunction<C, D>,
mixinF: UnaryFunction<E, F>,
mixinG: UnaryFunction<F, G>
): G
export function compose<T extends Constructor, A, B, C, D, E, F, G, H>(
superclass: T,
mixin: UnaryFunction<T, A>,
mixinB: UnaryFunction<A, B>,
mixinC: UnaryFunction<B, C>,
mixinD: UnaryFunction<C, D>,
mixinF: UnaryFunction<E, F>,
mixinG: UnaryFunction<F, G>,
mixinH: UnaryFunction<G, H>
): H
export function compose<T extends Constructor, A, B, C, D, E, F, G, H, I>(
superclass: T,
mixin: UnaryFunction<T, A>,
mixinB: UnaryFunction<A, B>,
mixinC: UnaryFunction<B, C>,
mixinD: UnaryFunction<C, D>,
mixinF: UnaryFunction<E, F>,
mixinG: UnaryFunction<F, G>,
mixinH: UnaryFunction<G, H>,
mixinI: UnaryFunction<H, I>
): I
export function compose(
superclass: Constructor,
...mixins: Array<UnaryFunction<any, any>>
): UnaryFunction<any, any> {
return mixins.reduce((c, mixin) => mixin(c), superclass)
}

0 comments on commit 8007301

Please sign in to comment.