If you have a reasonable maximum on the number of properties you can accept, you might want to just have the compiler calculate these properties for you. For example, the following will generate item1 through item249:
type Foo = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 10
type Bar = [...Foo, ...Foo, ...Foo, ...Foo, ...Foo]; // 50
type Baz = [...Bar, ...Bar, ...Bar, ...Bar, ...Bar]; // 250
type ItemNames = `item${Exclude<keyof Baz, '0' | keyof any[]>}`
// item1 through item249
type ItemProps = { [K in ItemNames]?: any };
interface Items extends ItemProps {
}
const obj: Items = {
item1: 'foo',
item2: 'bar',
item3: 'baz',
} // okay
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
};
You can scale this up to maximums less than about 10,000 before the compiler starts complaining:
type Foo = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 10
type Bar = [...Foo, ...Foo, ...Foo, ...Foo, ...Foo]; // 50
type Baz = [...Bar, ...Bar, ...Bar, ...Bar, ...Bar]; // 250
type Qux = [...Baz, ...Baz, ...Baz, ...Baz, ...Baz]; // 1250
type Quux = [...Qux, ...Qux, ...Qux, ...Qux, ...Qux]; // 6250
// type Suux = [...Quux, ...Quux]; // error:
// Type produces a tuple type that is too large to represent.
type ItemNames = `item${Exclude<keyof Quux, '0' | keyof any[]>}`
// item1 through item6249
Playground link to code