What I am trying to do is create a simple function that would optionally accept a transformer
method as input and return:
transformer
provided)transformer
's return type.A small example to showcase what I mean is the following:
// our basic example interface
interface Person {
name: string;
age: number
};
// the transformer fn type
type TransformerFn = <T>(person: Person) => T;
// method options contaning the optional transformer method param
interface mapperOpts {
paramA?: number
transformer?: TransformerFn
}
// Conditional type for returning the result based on providing transformer in options or not
type OriginalOrTransformedPerson<T extends Partial<mapperOpts>> = T extends { transformer: TransformerFn } ?
ReturnType<T['transformer']> :
Person;
// test class interface
interface PeopleGetter {
getPeople<T extends Partial<mapperOpts>>(people: Person[], opts: T): OriginalOrTransformedPerson<T>
}
class Test implements PeopleGetter {
getPeople(people: Person[], opts: Partial<mapperOpts>) {
if (opts.transformer) {
return people.map(opts.transformer);
} else {
return people;
}
}
}
const people: Person[] = [{ name: 'john', age: 20 }];
const test = new Test();
const original = test.getPeople(people); // here we should have `Person[]`
const transformedResult = test.getPeople(people, { transformer: (person: Person) => person.name }); // here I would like to have the return type of 'string[]' based on transformer method
Typescript playground here
I tried to follow the plain map
implementation of Array<T>
interface which properly infers the return type of the provided callback method in map
but I could not get it to work. I get errors in getPeople
implementation and usage.
Any ideas?
I tried to simplify and create a more generic version of the problem and posted it here: Typescript method return type based on optional param property function
With the really generous help and explanation of @jcalz I was able to also create a version that works for my original post here. The thing it that Type Inference is a really complicated matter for Typescript and not everything is automatically inferred as someone would expect to. Important conclusions are:
Having said that, here is the version that works for my original question:
// our basic example interface
interface Person {
name: string;
age: number
};
// the transformer fn type
type TransformerFn<T = any> = (person: Person) => T;
// method options contaning the optional transformer method param
interface mapperOpts<T = any> {
paramA?: number
transformer?: TransformerFn<T>
}
// Conditional type for returning the result based on providing transformer in options or not
type OriginalOrTransformedPerson<T> = T extends { transformer: TransformerFn<infer R> } ?
R :
Person;
// test class interface
interface PeopleGetter {
getPeople<T extends Partial<mapperOpts>>(people: Person[], opts: T): OriginalOrTransformedPerson<T>[]
}
class Test implements PeopleGetter {
getPeople<T extends Partial<mapperOpts>>(people: Person[], opts?: T): OriginalOrTransformedPerson<T>[] {
if (opts?.transformer) {
return people.map(opts.transformer);
} else {
return people as OriginalOrTransformedPerson<T>[];
}
}
}
const people: Person[] = [{ name: 'john', age: 20 }];
const test = new Test();
const original = test.getPeople(people); // here we should have `Person[]`
const transformedResult = test.getPeople(people, { transformer: (person: Person) => person.name }); // here I would like to have the return type of 'string[]' based on transformer method