As an example, say I have a simple function which maps a variadic number of things to an array of objects like { a: value }
.
const mapToMyInterface = (...things) => things.map((thing) => ({ a: thing}));
Typescript in not yet able to infer a strong type for the result of this function yet:
const mapToMyInterface = mapToInterface(1, 3, '2'); // inferred type{ a: any }[]
First, I define a type that describes an array mapped to observables:
type MapToMyInterface<T extends any[]> = {
[K in keyof T]: T[K] extends Array<infer U> ? { a: U } : { a: T[K] }
}
Now I update my function:
const mapToMyInterface = <T extends any[]>(...things: T): MapToMyInterface<T> => things.map((thing) => ({ a: thing}));
So far, Typescript is not happy. The return expression of the function is highlighted with the error "TS2322: Type '{ a: any; }[]' is not assignable to type 'MapToMyInterface'"
Obviously, the parameter thing
needs to be explicitly typed in the mapping function. But I don't know of a way to say "the nth type", which is what I need.
That is to say, neither marking thing
as a T[number]
or even doing the following works:
const mapToMyInterface = <T extends any[], K = keyof T>(...things: T): MapToMyInterface<T> => things.map((thing: T[K]) => of(thing));
Is is possible for this to work in Typescript?
EDIT after @jcalz's answer: For posterity, I wanted to post the original motivation for my question, and the solution I was able to get from @jcalz's answer.
I was trying to wrap an RxJs operator, withLatestFrom
, to lazily evaluate the observables passed into it (useful when you may be passing in an the result of a function that starts an ongoing subscription somewhere, like store.select
does in NgRx).
I was able to successfully assert the return value like so:
export const lazyWithLatestFrom = <T extends Observable<unknown>[], V>(o: () => [...T]) =>
concatMap(value => of(value).pipe(withLatestFrom(...o()))) as OperatorFunction<
V,
[V, ...{ [i in keyof T]: TypeOfObservable<T[i]> }]
>;