Reputation: 1762
If I write this:
declare function callbackFn<T>(entry: T): T extends string ? "string !" : "not string!";
const output= ([ "foo", 42 ] as const).map(callbackFn);
the type of output
is ("string !" | "not string!")[]
is there any way to get it as readonly [ "string !", "not string!" ]
?
Thank you
Upvotes: 1
Views: 151
Reputation: 33051
It is possible to achieve with help of function-overloads.
First of all, lets define callbackFn
:
type CallbackFn<T> = T extends string ? "string !" : "not string!";
function callbackFn<T>(entry: T): CallbackFn<T>
function callbackFn<T>(entry: T) {
return typeof entry === 'string' ? "string !" : "not string!"
}
I have overloaded callbackFn
and used CallbackFn
type utility to infer return type.
Now, we need type utility for our mapping. Consider this:
type MapUtility<T extends ReadonlyArray<unknown>> = {
[Prop in keyof T]: CallbackFn<T[Prop]>
}
type Test1 = MapUtility<["foo", 42]> // ["string!", "not string!"]
Since all utils are set we can write our main function. Keep in mind, that it is not necessary to use as const
to infer literal argument. You can use variadic-tuple-types:
function map<
Elem,
Tuple extends Elem[]
>(tuple: [...Tuple]): MapUtility<Tuple>
function map(tuple: unknown[]) {
return tuple.map(callbackFn)
}
// ["string !", "not string!"]
const output = map(["foo", 42])
Whole example:
type CallbackFn<T> = T extends string ? "string !" : "not string!";
function callbackFn<T>(entry: T): CallbackFn<T>
function callbackFn<T>(entry: T) {
return typeof entry === 'string' ? "string !" : "not string!"
}
type MapUtility<T extends ReadonlyArray<unknown>> = {
[Prop in keyof T]: CallbackFn<T[Prop]>
}
type Test1 = MapUtility<["foo", 42]> // ["string!", "not string!"]
function map<
Elem,
Tuple extends Elem[]
>(tuple: [...Tuple]): MapUtility<Tuple>
function map(tuple: unknown[]) {
return tuple.map(callbackFn)
}
// ["string !", "not string!"]
const output = map(["foo", 42])
Please keep in mind that typescript does not preserve tuple length after map
. See this answer.
This answer is related
P.S. If you are interested in function argument inference in TypeScript you can check my article
Upvotes: 1
Reputation: 81
If you are working with tuple types, I believe you have to avoid .map, try this instead:
const mapTuple = (tuple: [unknown, unknown]) => [callbackFn(tuple[0], callbackFn(tuple[1])] as const;
This way you still keep the tuple type as a return type.
Upvotes: 1