I'm trying to learn more about conditional types in TS and, as a small challenge, decided to write something like a generic mapper. But I have stuck with an error
What I need to do:
- into
createMapper
function need to pass a mapper; - from
createMapper
return a function which input property can receive a value or an array of values; - after passing a value ts compiler must explicitly understand what type is returned from a function;
something like:
type MapI = { abc: 123 };
const mapI: MapI = { abc: 123 };
const mappedArray = mapps<MapI, MapI>((m) => m)(Array(3).fill(mapI));
console.log(mappedArray.length); // no errors, compiler understands that it is an array;
const mappedValue = mapps<MapI, MapI>((m) => m)(mapI);
console.log(mappedValue.abc); // no errors, compiler understands that it is an object;
Here is a code of this function, it can be a little bit ugly, but it's only the first implementation of this function:
function createMapper<In, Out>(
mapper: (input: In) => Out
): <Source extends In | In[]>(source: Source) => Source extends In[] ? Out[] : Out {
return (source) => Array.isArray(source)
? source.map(mapper)
: mapper(source);
}
And I have no errors in type recognition by ts compiler but I have an error in function implementation.
Type 'Out | Out[]' is not assignable to type 'Source extends In[] ? Out[] : Out'.
Type 'Out' is not assignable to type 'Source extends In[] ? Out[] : Out'
This function is available in the playground:
Can anyone, please, explain why this error happening and maybe the method to fix it.
Thank you and have a nice day!
CodePudding user response:
Only function overloading in such cases:
function createMapper<In, Out>(
mapper: (input: In) => Out
) {
function condition<Source extends In | In[]>(source: Source): Source extends In[] ? Out[] : Out
function condition(source: In | In[]) {
return Array.isArray(source)
? source.map(mapper)
: mapper(source);
}
return condition
}
type MapI = { abc: 123 };
const mapI: MapI = { abc: 123 };
const mappedArray = createMapper<MapI, MapI>((m) => m)(Array(3).fill(mapI)); // MapI[]
const mappedValue = createMapper<MapI, MapI>((m) => m)(mapI); // MapI
COnsitional types in a place of return type does not supported by TS