Search code examples
typescriptunionstring-literals

How to create a string literal type for keys of an indexable type in TypeScript?


I have an indexable type in TypeScript with keys that can either be strings or numbers, like so:

export type MyRecords = { [name: string]: string | number };

const myRecords: MyRecords = {
  foo: 'a',
  bar: 1,
};

I want to create a string literal type that includes only the string keys of this type, so that I can use it to ensure type safety in my code. For example:

type KeysOfMyRecords = /* ??? */;

const key: KeysOfMyRecords = 'foo'; // should be OK
const invalidKey: KeysOfMyRecords = 'invalid'; // should cause a  error

I have tried the following without success:

type KeysOfMyRecords = keyof typeof myRecords;

Would this be possible?


Solution

  • I think you can use a generic to achieve this:

    export type MyRecords<T extends string | number | symbol> = Record<T, string | number>
    
    const myRecords : MyRecords<'foo' | 'bar'>= {
      foo: 'a',
      bar: 1,
    };
    
    
    type KeysOfMyRecords = keyof typeof myRecords;
    
    const key: KeysOfMyRecords = 'foo'; // should be OK
    const invalidKey: KeysOfMyRecords = 'invalid'; // should cause a  error
    

    The downside as you saw above is that you will have to mention the keys while defining your object.

    Link

    The issue with your expectation is that we want myRecords to be of a certain type MyRecords. Now we have explicityle defined MyRecords type to be having string as key, and that means any string. We could also have used const assertion in your code directly but it would not guarantee type safety with MyRecords type.