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.
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