I think code is the best way to explain what I'm trying to do:
interface Input { in: string }
interface Output { out: string }
function doStuff(input: Input): Output {
return { out: input.in };
}
function f<Out>(input: Input, transform?: (data: Output) => Out) /* result-type ? */ {
const output = doStuff(input);
if(transform) return transform(output);
else return output;
}
f({in: 'simple'}).out == 'simple';
f({in: 'with transform'}, (data: Output) => data.out) == 'with transform';
I'm struggling to get the return type of the function f
right. Basically I want f
to return Output
when transform
is undefined and the result type of transform
when it is given.
I tried using different declarations for both cases, and conditional types, but there always is some issue. Can this be modeled with typescript ?
CodePudding user response:
I tried using different declarations for both cases, and conditional types, but there always is some issue. Can this be modeled with typescript?
By "different declarations", I presume that you mean function overloads (which work for your case).
This cannot currently be modeled using conditional types, because TypeScript is not yet capable of inferring type information from optional parameters which are constrained generics. But if it could, your conditional version would look something like this (doesn't work!):
type Transform<I, O> = (data: I) => O;
type Input<T> = { in: T };
type Output<T> = { out: T };
function doStuff <T>(input: Input<T>): Output<T> {
return { out: input.in };
}
function f <T, Fn extends Transform<Output<T>, any>>(
input: Input<T>,
...[transform]: [] | [transform: Fn]
): typeof transform extends undefined ? Output<T> : ReturnType<Fn> {
const output = doStuff(input);
return transform ? transform(output) : output;
}
f({in: 'simple'}).out === 'simple'; // any (incorrectly inferred from the generic constraint)
f({in: 'with transform'}, data => data.out) === 'with transform'; // string (correctly inferred)
CodePudding user response:
Two overloads do the trick:
function f(input: Input): Output;
function f<T extends (data: Output) => unknown>(input: Input, transform: T): ReturnType<T>;
function f<T extends ((data: Output) => unknown) | undefined>(input: Input, transform?: T) {
const output = doStuff(input);
if(transform) return transform(output);
else return output;
}