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 }>