Search code examples
typescript

Use record key type in value function


The following code doesn't work, as the converter function by Typescript should expect a parameter typed Filters. How can we make this work, so that the argument is based on the type of the key of the object?

type Filter1 = {
  type: 'filter1',
  value1: number
}

type Filter2 = {
  type: 'filter2',
  value2: number;
}

type Filters = Filter1 | Filter2;

const converter: Record<Filters['type'], (filter: Filters) => string> => {
  // I'd want: 
  // filter1: (filter: Filter1) => {
  filter1: (filter: Filter) => {
     return (filter as Filter1).value1.toString(); 
  },
  // filter2: (filter: Filter2) => {
  filter2: (filter: Filter) => {
     return (filter as Filter2).value2.toString(); 
  },
}

Solution

  • You don't want to use the Record<K, V> type, because the keys and value types of Record are independent of each other. If you want the property type at a key to depend on the key, then you need a more general mapped type.

    In particular you seem to be looking for a key remapped type where you iterate over the elements of the Filter union and use its type property as the key:

    type Converter = { [T in Filters as T['type']]: (filter: T) => string }
    /* type Converter = {
        filter1: (filter: Filter1) => string;
        filter2: (filter: Filter2) => string;
    } */
    
    const converter: Converter = {
      filter1: filter => {
        return filter.value1.toString();
      },
      filter2: filter => {
        return filter.value2.toString();
      },
    }
    

    That now behaves as desired, and note that the filter callback parameters are contextually types by Converter so you don't have to annotate them.

    Playground link to code