Reputation: 6269
Consider the following:
function makeOutputArrayBasedOnInputArray<T>(arrayOfStuff: T[]) {
return arrayOfStuff.map(item => ({ item }));
}
const x = makeOutputArrayBasedOnInputArray([1, 'two']);
const y = x[0] // { item: string | number; }
const z = x[1] // { item: string | number; }
What I'd love is instead for the types to be surfaced:
const y = x[0] // { item: number; }
const z = x[1] // { item: string; }
So imagine the doIt
function was returning a tuple type which is entirely generic and of varying length; driven by the input to the function. So this would work too:
const x = makeOutputArrayBasedOnInputArray([7, 'nine', Date]);
const a = x[0] // { item: number; }
const b = x[1] // { item: string; }
const c = x[2] // { item: Date; }
Is this possible? Or have I reached the limits of infer
and tuples?
See also: Variable length Array tuple in TypeScript? and Typescript, generic variadic factory function returning tuple
Hello and thanks for your answer! Whilst it does answer the question, it relies on the caller using as const
. Without the as const
, alas it doesn't work.
This may turn out to be key. Some context: the question behind the question is a PR I've raised to improve the type definitions of react-query: https://github.com/tannerlinsley/react-query/pull/1527/files
The change at present looks like this:
export function useQueries<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData
>(
queries: UseQueryOptions<TQueryFnData, TError, TData>[]
): UseQueryResult<TData, TError>[] {
// ...
}
And this is great but has the non-specific type inference problem. Ideally we'd like this behaviour:
const result = useQueries([
{ queryKey: key1, queryFn: () => 1 },
{ queryKey: key2, queryFn: () => 'two' },
])
// result[0].data => number | undefined
// result[1].data => string | undefined
But without requiring the caller specifying as const
- which may not be possible?
Upvotes: 0
Views: 459
Reputation: 862
You can add an explicit function return type consisting of a mapped tuple type:
function fn<T extends readonly any[]>(arr: T): {[K in keyof T]: {item: T[K]} } {
return arr.map(item => ({ item })) as any;
}
const x = fn([1, 'two'] as const);
// x: readonly [{ item: 1; }, { item: "two"; }]
const y = x[0] // y: { item: 1; }
const z = x[1] // z: { item: "two"; }
Update: To narrow the type from the callee side, you can use variadic tuple types (TS 4.0):
function fn<T extends readonly any[]>(arr: [...T]): // <-- note [...T]
{[K in keyof T]: {item: T[K]} } {...}
Upvotes: 2