Home > Back-end >  Typing a function based on the argument value
Typing a function based on the argument value

Time:04-23

Suppose I have the following JS function:

function extractProperty(propName) {
    return x => x[propName];
}

Can I type extractProperty s.t. by Typescript's type inference I get the following:

const x = { first: 1, second: "two" };
const y = { first: [1], second: 2 };
// or any other object having first and second

const a1 = extractProperty("first")(x); // a1 should be of type number
const a2 = extractProperty("second")(x); // a2 should be string
const a3 = extractProperty("first")(y); // a3 should be number[]
const a4 = extractProperty("second")(y); // a4 should be number

const getFirst = extractProperty("first");
// getFirst should be <T> (t: T) => T["first"]

const getSecond = extractProperty("second");
// getSecond should be <T> (t: T) => T["second"]

If rather than a parameter propName, I had a fixed property name, I could probably do the following:

function extractPropertyFirst ():
<TFirst> (x: { first: TFirst }) => TFirst

or, slightly more in the direction of what I want to achieve,

function extractPropertyFirst ():
<T> (x: T) => T["first"]

Yet how can I make typescript take the argument of the function as the property name?

I have played around with the following approach, yet without success:

function getProperty<K> (propName: K):
<TProp> (t: { [K]: TProp }) => TProp {
    return x => x[propName];
}

Typescript does not accept a general K as the indexing type.

CodePudding user response:

You can make getProperty work by using {[Key in K]: TProp}, or equivalently, Record<K, TProp> for the type of t, and adding an K extends PropertyKey constraint:

function extractProperty<K extends PropertyKey>( propName: K):
  <TProp>(t: Record<K, TProp>) => TProp {
    return (x) => x[propName];
  }

const a1 = extractProperty("first")(x);
// type: number

const a2 = extractProperty("second")(x)
// type: string

const a3 = extractProperty("first")(y);
// type: number[]

const a4 = extractProperty("second")(y);
// type: number

TypeScript playground

  • Related