Allow Only Keys of Interface in TypeScript


In TypeScript, you can create an object with any keys or allow only certain keys.

Any Keys Allowed

To use any keys, indicate that any string is a valid key:

type TrackedValues = Record<string, boolean>

const tracked = { any: true, thing: true }

Only Allow Keys from an Interface

If you have an interface and want to use those as the only valid keys, you can do so:

export interface ValidValues {
  field1: string;
  field2: string;
}

type TrackedValidValues = { [key in keyof ValidValues]?: boolean }

const tracked: TrackedValidValues = { field1: true, fieldUnknown: true }

Running tsc on this code will yield the error:

Type '{ field1: true; fieldUnknown: boolean; }' is not assignable to type 'TrackedValidValues'.
  Object literal may only specify known properties, and 'fieldUnknown' does not exist in type 'TrackedValidValues'.(2322)

This is because fieldUnknown is not a key in the ValidValues interface.

The key here is in the typing of generic key must be in the array of keys on this interface, and that's shown in the dynamic property syntax of [K in keyof ValidValues].

Only Allow Keys from Union Type

If you have a union on string literals and want to use those as the only valid keys, you can do so:

type ValidValues = 'field1' | 'field2'

type TrackedValidValues = { [key in ValidValues]?: boolean }

const tracked: TrackedValidValues = { field1: true, fieldUnknown: true }

Running tsc on this code will yield the error:

Type '{ field1: true; fieldUnknown: boolean; }' is not assignable to type 'TrackedValidValues'.
  Object literal may only specify known properties, and 'fieldUnknown' does not exist in type 'TrackedValidValues'.(2322)

Similar to the interface, we are looking for inclusion of key in a collection of ValidValues, but ValidValues is already a collection, so we drop the keyof operator.

Also note here that TrackedValidValues must be a type definition and not an interfaced for this key syntax to work.

A Note on Parital Values of Union

There's a variation on the previous scenario that you may run into. What if you don't have, want or are not guaranteed all the values of ValidValues to be present in TrackedValidValues?

Let's say you only have field1 but not field2, like this:

const tracked: TrackedValidValues = { field1: true }

Currently this works because of the type definition for TrackedValidValues:

type TrackedValidValues = { [key in ValidValues]?: boolean }

It's that ? that does the trick. It says that the union values as object field keys are all optional. But if we removed that, tsc would cry about our tracked implementation:

Property `field2` is missing in type '{ field1: true }'

The alternate syntax to solve this problem is this:

type TrackedValidValues = Partial<{ [key in ValidValues]: boolean }>