SnailCrusher
SnailCrusher

Reputation: 1424

TypeScript: Can you define a return Type structure based on an argument of the function?

I'm trying to create a function that will take a string argument and return an object that has this string as a key.

for example (pseudo code): test('bar') => {bar: ...}

I'm not sure how to get the return type right for this function.

This code gives me the right return type, but the TS compiler doesn't think my returned object matches the return Type?

function test<K extends string>(key: K):{[prop in K]: number} {
  return { [key]: 6 } // error: not assignable
}
const foo = test<'bar'>('bar')
// foo type: {bar: number} // return type is good

Something like this will work fine. But does not give me the strongly types return type I am looking for:

function test2<K extends string>(key: K){
  return { [key]: 6 }
}
const foo2 = test2<'bar'>('bar')
// foo2 type: {[x: string]: number}  // no good

Any help with this would be much appreciated!

Upvotes: 3

Views: 1718

Answers (3)

Bellian
Bellian

Reputation: 2150

you can do lots of stuff with extended typings.

Like @SnailCrusher showed, you can define the return type staticly. There is also a way to dynamically assign typings to the returned props:

// this interface defines potential parameters to the methods

interface Tokens {
    foo: number,
    bar: string,
}



// return one prop in the result object
// this methods only accept keys of the interface Tokens as valid inputs

function test<K extends keyof Tokens>(key: K) {
    switch(key) {
        case 'foo': return { [key]: 0 } as {[prop in K]: Tokens[K]}
        case 'bar': return { [key]: '0' } as {[prop in K]: Tokens[K]};
    }
    return { [key]: undefined } as {[prop in K]: Tokens[K]}
}

const bar = test('bar') // { bar: string }
const foo = test('foo') // { foo: number }



// return full interface in the result object
// the given token will be set an all other props will be optional

function test2<K extends keyof Tokens>(key: K) {
    return { [key]: 6 } as {[prop in K]: Tokens[K]} & {[P in keyof Tokens]?: Tokens[P];}
}

const bar2 = test2('bar') // { foo?: number; bar: string; }
const foo2 = test2('foo') // { foo: number; bar?: string; }

This will allso add rich context to your IDE on valid parameters.

You can read mor of this in the Typescript documentation: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types-and-index-signatures

Upvotes: 2

MoxxiManagarm
MoxxiManagarm

Reputation: 9134

I don't understand why you need generics here, what speaks against simply doing

function test(key: string): { [key: string]: number } {
  return { [key]: 6 };
}

Upvotes: 1

SnailCrusher
SnailCrusher

Reputation: 1424

Tweaking the first attempt a bit and this seems to work:

function test<K extends string>(key: K) {
  return { [key]: 6 } as {[prop in K]: number}
}

const foo = test('bar') // { bar: number }

Having to cast it seems a bit strange to me though.

Upvotes: 2

Related Questions