Closed silasdavis closed 1 year ago
The following seems to work:
import * as t from 'io-ts';
type Schema<N extends string, A> = {
n: N;
s: t.Type<A>;
};
const schema1 = {
n: 'foo',
s: t.type({ foo: t.string }),
} as const;
const schema2 = {
n: 'bar',
s: t.type({ bar: t.string }),
} as const;
const schema3 = {
n: 'baz',
s: t.type({ baz: t.string }),
} as const;
export function maybeUnion<T extends t.Type<any>[]>(schemas: T) {
if (schemas.length === 0) {
throw new Error('Cannot union zero schemas');
}
if (schemas.length === 1) {
return schemas[0];
}
return t.union([schemas[0], schemas[1], ...schemas.slice(2)]);
}
// Distribute over union
export type OutputSchema<T> = T extends Schema<infer N, infer S>
? t.Type<{
n: N;
s: S;
}>
: never;
// Handle the branching between whehter we have one or more than one EventSchema types
export type OutputSchemas<T extends any[]> = T extends [infer First, infer Second, ...(infer Rest)[]]
? t.UnionC<[OutputSchema<First>, OutputSchema<Second>, ...OutputSchema<Rest>[]]>
: T extends [infer First]
? OutputSchema<First>
: never;
type SchemaTuple = [string, any];
function unionClosure<
TS extends [SchemaTuple, ...SchemaTuple[]],
SS extends { [K in keyof TS]: Schema<TS[K][0], TS[K][1]> },
>(schemas: SS): OutputSchemas<SS> {
return maybeUnion(schemas.map(({ n, s }) => t.type({ n: t.literal(n), s }))) as OutputSchemas<SS>;
}
const actualUnion = unionClosure([schema2, schema1]);
type Foo = OutputSchemas<[typeof schema1, typeof schema2]>;
I would like to type a function that returns a union based on some generic arguments containing a schema.
Below is a simplified example:
I was hoping to use variadic types and a map to get the same types as if I had written it out literally. I'm just wondering if this is actually possible?
(I appreciate in the above I am not maintaining the non-empty array property that
t.union
expects, but I could work around that in various ways if it was actually possible to typeunionClosure
as I want it at all)