Adam Maritz
Adam Maritz

Reputation: 3

Dynamic return type based on parameter to function

Im pretty new to Typescript and I've gotten stuck and im trying to understand if this is possible.

My goal is to be able to call my function "getRecords" with a specifc option property called "fields" where I input the fields I want to fetch from my server, and I want typescript to suggest the fieldnames I used for the input on the object that gets returned.

I have been trying to do this with a single field as an input but my ultimate goal is to use an array with field names.

Here is an example code, I also created a typescript playground:

const getRecords = async (options: { fields: string }): Promise<{ result: { [options.fields]: string}[] } | undefined> => {
    return await new Promise((resolve, reject) => {
        //Fetch Something here
        resolve({
            result: [
                { name: 'Test Name' }
            ]
        })
        reject(undefined)
    })
}

const test = async () => {
    let data = await getRecords({
        fields: 'name'
    })

    console.log(data)
}

test()

The only time I got it to work was when I hardcoded the key in the returning promise like this:

Promise<{ result: { ['name']: string}[] } | undefined>

Would really appreciate any help with this

Thanks!

Upvotes: 0

Views: 1529

Answers (1)

Aron
Aron

Reputation: 9248

Something like this will do what I think you're after:

const makeKeys = <T extends string>(fields: T[]): Record<T, string> => {
    // Sample implementation

    const map: Record<T, string> = {} as Record<T, string>
    for (const field of fields) {
        map[field] = 'test value';
    }

    return map;
}

const obj = makeKeys(['test', 'thing']);

// These fields can now be safely accessed with autocomplete :)
obj.thing // string
obj.test  // string

The key things here are we specify that we're passing an array of strings to the function

<T extends string>(fields: T[])

and that the function will return a Record type whose keys are the very same strings contained in the array

Record<T, string>

Once TS knows that the type system can infer that whichever strings you've passed into the input array will be the keys of the output object.

// These fields can now be safely accessed with autocomplete :)
obj.thing // string
obj.test  // string

Upvotes: 1

Related Questions