typescript template literal as interface key

UPDATE FOR TS4.4+

TypeScript 4.4 will support index signatures that include pattern template literals, as implemented in microsoft/TypeScript#44512. You will then be able to declare Items as a specific type, like this:

interface Items {
  [key: `item${number}`]: any;
}

And you can verify that it works as desired:

const obj: Items = {
  item1: 'foo',
  item2: 'bar',
  item2021: 'baz',
  item3: 'qux',
};

const objBad: Items = {
  item1: 'foo',
  item2: 'bar',
  itemMMXXI: 'baz', // error!
  //  ~~~~~~~~~ <--
  //  Object literal may only specify known properties,
  //  and 'itemMMXXI' does not exist in type 'Items'
  item3: 'qux'
};

Playground link to code


ANSWER FOR TS4.1-4.3

Pattern template literals of the form `item${number}` (as implemented in microsoft/TypeScript#40598) are not currently allowed as key types, as of TypeScript 4.1.

For now there is no specific type corresponding to your desired Items type. Instead, you could represent it as a constraint on a type and write a helper function asItems() which will only accept inputs that adhere to the constraint:

const asItems = <K extends PropertyKey>(
    obj: { [P in K]: P extends `item${number}` ? any : never }
) => obj;

Each key of the passed-in obj will be checked for whether it is assignable to `item${number}`. If so, the property type is any, and if not, the property type is never. That will tend to cause errors on any offending property:

const obj = asItems({
    item1: 'foo',
    item2: 'bar',
    item2021: 'baz',
    item3: 'qux',
}); // okay

const objBad = asItems({
    item1: 'foo',
    item2: 'bar',
    itemMMXXI: 'baz', // error!
//  ~~~~~~~~~ <-- Type 'string' is not assignable to type 'never'
    item3: 'qux'
});

Playground link to code

Leave a Comment