Currently, I am in the process of learning typescript and facing challenges in understanding how to correctly type a reducer function that reduces functional mixins.
Let's consider two functional mixins:
type FooComposable = {
foo: string;
};
const withFoo = (composable): FooComposable => {
composable.foo = 'foo';
return composable;
};
type BarComposable = {
bar: string;
};
const withBar = (composable): BarComposable => {
composable.bar = 'bar';
return composable;
};
I have implemented a reducer function that will reduce all provided functional mixins:
const reduce(...fns) = fns.reduce((acc, fn) => fn(acc), {}));
reduce(withFoo); // -> { foo: 'foo' }
reduce(withBar); // -> { bar: 'bar' }
reduce(withFoo, withBar); // -> { foo: 'foo', bar: 'bar' }
My main concern is adding typings to the reduce()
function (and functional mixins) in order to ensure that a reduced composable has the expected type inferences.
type FooComposable = {
foo: string;
};
const withFoo = <T extends FooComposable>(composable: T): FooComposable => {
composable.foo = 'foo';
return composable;
};
type BarComposable = {
bar: string;
};
const withBar = <T extends BarComposable>(composable: T): BarComposable => {
composable.bar = 'bar';
return composable;
};
type FunctionalMixin<T extends {}> = (composable: T) => T;
const reduce = <T extends {}>(...fns: FunctionalMixin<T>[]): T =>
fns.reduce((acc, fn) => fn(acc), {});
/* Type '{}' is not assignable to type 'T'.
'{}' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'. */
reduce<FooComposable & BarComposable>(withFoo, withBar); // -> { foo: 'foo', bar: 'bar' }
/* Argument of type '<T extends FooComposable>(composable: T) => FooComposable' is not assignable to parameter of type 'FunctionalMixin<FooComposable & BarComposable>'.
Type 'FooComposable' is not assignable to type 'FooComposable & BarComposable'.
Property 'bar' is missing in type 'FooComposable' but required in type 'BarComposable'. */