Is there a way to safely implement the given function in TypeScript without using unsafe casts or an extensive number of function overloads with various input permutations?
interface Wrapped<T> {
type: string;
data: T;
}
interface WrappedA<T> extends Wrapped<T> {
type: "a";
}
interface WrappedB<T> extends Wrapped<T> {
type: "b";
}
type FuncProps<A = never, B = never> = { a?: A[]; b?: B[] };
const func = <A = never, B = never>({ a, b }: FuncProps<A, B>) => {
const ret: Array<([A] extends [never] ? never : WrappedA<A>) | ([B] extends [never] ? never : WrappedB<B>)> = [];
if (a != null) {
const push: Array<WrappedA<A>> = a.map(value => ({ type: "a" as const, data: value }));
ret.push(...push); // Error: Type 'WrappedA<A>' is not assignable to type '[A] extends [never] ? never : WrappedA<A>'.
}
if (b != null) {
const push: Array<WrappedB<B>> = b.map(value => ({ type: "b" as const, data: value }));
ret.push(...push); // Error: Type 'WrappedB<B>' is not assignable to type '[B] extends [never] ? never : WrappedB<B>'.
}
return ret;
};
// The Intended Result
const ret1 = func({ a: [1] }); // type: WrappedA<number>[]
const ret2 = func({ b: ["1"] }); // type: WrappedB<string>[]
const ret3 = func({ a: [1], b: ["1"] }); // type: (WrappedA<number> | WrappedB<string>)[]
I attempted using never
to exclude certain types from the union type of the returned array but encountered difficulties.