Home > OS >  TypeScript function return a value of a key which has a special type
TypeScript function return a value of a key which has a special type

Time:11-03

Let's say I have an object like this

const person = {
    name: 'Amir',
    age: 25,
    favoriteFruits: ['Apple', 'Orange']
};

I need a function to accept a key which its value is an array.

Here the second argument in IDE should just suggest the favoriteFruits key:

get(person, '');

I tried to make such function but it accepts all of the keys

function get<T, Key extends keyof T>(container: T, key: Key) {
    return container[key];
}

CodePudding user response:

Something like this?

function get<K, T extends {[K]: Array<any>}>(container: T, key: K) {
  return container[key];
}

CodePudding user response:

Using KeysOfType from this post:

type KeysOfType<O, T> = {
  [K in keyof O]: O[K] extends T ? K : never
}[keyof O];

We can define get as:

function get<T = any>() {
    return function <C, Key extends KeysOfType<C, T>>(container: C, key: Key) {
       return container[key];
    }
}

Then use it:

get<string[]>()(person, 'age'); // ERROR - 'age' doesn't point to a string[]
get<string[]>()(person, 'favoriteFruits'); // OKAY

Note that currying is required here since partial type inference is not supported yet.

Playground

CodePudding user response:

If you use the following helper type:

type RemoveNonArrayKeys<T> = keyof {
  [K in keyof T as T[K] extends unknown[] ? K : never]: T[K];
}

It will filter the keys that are not arrays, after that you only need to redefine your function get as:

function get<T>(container: T, key: RemoveNonArrayKeys<T>) {
    return container[key];
}

The whole example with some extra test:

const person = {
    name: 'Amir',
    age: 25,
    favoriteFruits: ['Apple', 'Orange'],
    // Test
    favoriteNumbers: [1, 2, 3]
};

type RemoveNonArrayKeys<T> = keyof{
  [K in keyof T as T[K] extends unknown[] ? K : never]: T[K];
}

type onlyArrayPersonKeys = RemoveNonArrayKeys<typeof person>;
//   ^?   "favoriteFruits" | "favoriteNumbers"

function get<T>(container: T, key: RemoveNonArrayKeys<T>) {
    return container[key];
}

get(person, 'favoriteFruits');
get(person, 'favoriteNumbers'); // It also is validated since the value of the key is a valid array
get(person, 'name'); // Error

TS Playground

  • Related