My current project involves developing a TypeScript function that can return different objects based on the input parameter provided. The return type of this function is a tuple with the first two elements being a number and a string, and the third element being an object. The structure of the returned object varies depending on the specific input received. Here's a simplified version of the function I'm working on:
function findObject(n: number) {
if (n == 2)
return [0, "", { some: "data" }];
if (n == 3)
return [0, "", { next: "step" }];
return [0, "", { other: "some" }];
}
function getCustomObject() {
return [1, "some", {bebe: "baba"}]
}
I am aiming for TypeScript to deduce the type of the third element in the tuple ({ some: "data" }
, { next: "step" }
, or { other: "some" }
) based on the value of n
. This way, when working with the return value of the findObject
function, TypeScript should be able to offer autocompletion suggestions for the fields of the object, guided by the inferred return type.
For instance:
const result = findObject(2);
console.log(result[2].some); // Should recognize 'some' as a valid field
const customResult = getCustomObject()
console.log(customResult[2].bebe)
However, I've encountered difficulty in defining the return type of the findObject
function in a manner that allows TypeScript to correctly infer potential object structures without explicitly listing all possible return types beforehand.
Is there a technique in TypeScript to dynamically ascertain the structure of an object within a tuple return type based on runtime conditions? My aim is to achieve this without having to manually enumerate all conceivable object shapes as part of the function's return type declaration.
I tried implementing the following approach, but encountered the error message "Return type annotation circularly references itself."
function findObject(n: number): [string, Record<keyof (ReturnType<typeof findObject>[2]), any>] {
if (n == 2) return ["", { some: "data" }]
if (n == 3) return ["", { next: "step" }]
return ["", { other: "some" }]
}
Update
I managed to find a solution for my problem, but now I have a question - how can I achieve this without creating a new variable during the function declaration phase?
function determineObjectType<D, K extends keyof any>(func: (args: any) => [number, string, Partial<Record<K, D>>]) {
return func as (args: any) => { 0: number, 1: string, 2: Partial<Record<K, D>> };
}
const utilityFunc = determineObjectType(function some(a: number) {
if (a == 1)
return [0, "", { next: "me" }]
return [0, "", { some: "data" }]
})
utilityFunc(1)[2].
// -----------^next?: string
// -----------^some?: string