Reactgular
Reactgular

Reputation: 54821

How to define return type using keyof accessor with TypeScript?

Is it possible to create a function that uses keyof to access a property of an object, and have TypeScript infer the return value?

interface Example {
    name: string;
    handles: number;
}

function get(n: keyof Example) {
   const x: Example = {name: 'House', handles: 8};
   return x[n];
}

function put(value: string) {

}

put(get("name"));
// ^^^ Error: Argument of type 'string | number' is not assignable to parameter of type 'string'

TypeScript is merging all the allowed types together, but when the function is called with the value "name" it does not infer the type is string.

I can resolve the issue by casting the type.

put(get("name") as string);

Is there a way to do this without casting?

Upvotes: 4

Views: 3237

Answers (1)

jcalz
jcalz

Reputation: 330316

Sure, you just have to make get generic so that it doesn't return the union of all possible output types:

function get<K extends keyof Example>(n: K) {
  const x: Example = { name: "House", handles: 8 };
  return x[n];
}

Now the signature is inferred to be:

// function get<K extends "name" | "handles">(n: K): Example[K]

Where the return type, Example[K], is a lookup type representing the type you get when you index into an Example with key K.

When you call it, K is inferred to be a string literal type if it can be:

get("name"); // function get<"name">(n: "name"): string

And Example["name"] is equivalent to string. So now your call works as desired:

put(get("name")); // okay

Hope that helps. Good luck!

Link to code

Upvotes: 12

Related Questions