Make a generic type Array<keyof T> require all keys of T

don picture don · Aug 26, 2018 · Viewed 7.1k times · Source

I would like to declare a type that requires all the keys of a given type T to be included in an array, e.g.:

checkKeys<T>(arr: Array<keyof T>): void {
  // do something
}

interface MyType {
  id: string;
  value: number;
}

Currently if a call checkKeys<MyType>, TS will consider the value passed as valid if it contains any key of MyType (id | value):

checkKeys<MyType>(['id', 'value']); // valid

checkKeys<MyType>(['id']); // valid

checkKeys<MyType>(['id', 'values']); // invalid

Is it possible to require that all keys are specified in the array?

Answer

Titian Cernicova-Dragomir picture Titian Cernicova-Dragomir · Aug 26, 2018

You can't do that with an array type (at least I am not aware of a way to spread the union of keys into a tuple type, there may be one I'm just not aware of it). An alternative would be to use an object literal to achieve a similar effect. The syntax is a bit more verbose but the compiler will validate that only the correct keys are specified. We will use the Record mapped type and we can use the 0 literal types for values as only the keys matter.

function checkKeys<T>(o: Record<keyof T, 0>): void {
     // do something
}

interface MyType {
    id: string;
    value: number;
}

checkKeys<MyType>({ id: 0, value: 0 }); // valid

checkKeys<MyType>({ id: 0 }); // invalid

checkKeys<MyType>({ id: 0, values: 0 }); // invalid