Search code examples
typescripttypesrecord

How to make a Record in TypeScript can accept different types


I want to ask about Record in TypeScript, currently when I use Record as parameter type in a function then I get error in my code because it can't accept different type.

type Keys = 'name' | 'qty';

const getValueByKey = <T = any>(items: Record<Keys, T>, key: Keys) => {
  return items[key];
}
getValueByKey({ name: 'fulan', qty: 1}, 'name'); // Error: Type 'number' is not assignable to type 'string'.(2322)

TypeScript Playground

The goal is actually to get the type of the value in the function.

How can I make a Record accept values of different types? Thank you


Solution

  • It seems like what you want to do is to accept an object type that has the keys in your union, and return a value from the object based on the key parameter while also inferring what constitutes a valid key parameter from the object that is provided. This type of inference is possible using generic constraints.

    Keep in mind that TypeScript is structurally typed, so any object that has the keys name and qty will be allowed (even if it has additional keys), and you'll be able to use any key from that object for the key argument.

    TS Playground

    type Key = 'name' | 'qty';
    
    function getValueByKey <
      T extends Record<Key, any>,
      K extends keyof T,
    >(items: T, key: K): T[K] {
      return items[key];
    }
    
    const obj = {name: 'fulan', qty: 1};
    
    const value1 = getValueByKey(obj, 'name');
        //^? const value1: string
    
    const value2 = getValueByKey(obj, 'qty');
        //^? const value2: number
    
    
    

    and if you try to use a key that's not in the object, TS will (appropriately) emit a compiler diagnostic error:

    const value3 = getValueByKey(obj, 'not_a_key');
    //                                ~~~~~~~~~~~
    // Argument of type '"not_a_key"' is not assignable to parameter of type '"name" | "qty"'. (2345)