Search code examples
typescripttypescript-generics

Use keyof Record as type of another key in the same interface


I have the following interfaces:

interface Values {
  height: number;
  width: number;
}

interface Opts {
  requiredKeys: (keyof Values)[];
  customParsers: Record<string, (text: string) => unknown>;
}

I want to make it so Opts['requiredKeys'] can also take strings from the keys of Opts['customParsers']; for example:

const opts: Opts = {
  requiredKeys: [
    'height',
    'foo', // error! not a key of `Values` and also not defined in `customParsers`
    'x', // allowed because `x` is defined in `customParsers`
  ],
  customParsers: {
    x: text => parseInt(text),
  },
};

I've tried changing Opts['requiredKeys']'s type to (keyof Opts['customParsers'] | keyof Values)[] but it didn't work.

I believe what I am trying to do can be achieved with generics but I am not very experienced with them and don't know how I'd implement it exactly.


Solution

  • The first answer has answered most of your question, except one point you mentioned:

    // error! not a key of Values and also not defined in customParsers

    So i came up with the solution for that problem aswell: Playground link

    interface Opts<T extends string> {
      requiredKeys: (keyof Values | NoInfer<T>)[]; // T will not be inferred here, instead will be inferred in the Record type
      customParsers: Record<T, (text: string) => unknown>;
    }
    
    
    function createOption<T extends string>(requiredKeys: Opts<T>["requiredKeys"], customParsers: Opts<T>["customParsers"]): Opts<T> {
      return { requiredKeys, customParsers };
    }
    
    
    const optsNew = createOption(
      [
        'x',
        'foo' // Type '"foo"' is not assignable to type 'keyof Values | "x"'
      ],
      {
        x: (text) => parseInt(text),
      },
    );