Reputation: 183
I have an enum:
enum Vehicles {
CAR = 1,
BOAT = 2,
AIRPLANE = 3
}
and an array that contains a subset of that enum:
const vehiclesThatFly: VehiclesThatFly = [Vehicles.AIRPLANE]
How can I make a Typescript type, VehiclesThatFly
, that will warn me when a new enum member is added to the enum so that I am forced to either 1) add that new member to my vehiclesThatFly
array or 2. explicitly exclude it in the VehiclesThatFly
type? In other words, if a new enum member is added, I do not want to have to remember to add it to vehiclesThatFly
, I want TS to tell me that enum hasn't been handled.
I am imagining the VehiclesThatFly
type would look something like this pseudocode: Exclude<(Vehicles enum values), Vehicles.CAR | Vehicles.BOAT>
.
Is it possible to create a type like VehiclesThatFly
or is there a better way to achieve my goal of having Typescript force me to explicitly handle new enum members?
Upvotes: 1
Views: 311
Reputation: 187024
You could do something like:
type VehiclesSubset<T extends Record<Vehicles, boolean>> = {
[K in keyof T]:
T[K] extends true ? K : never
}[keyof T]
Here we have a generic type that accepts a type of vehicle/boolean pairs. It maps over these pairs, and if the value T[K]
is true
then uses that key K
as the value, or if it is false
then resolve to never
. Then we index this mapped type by its own keys to get a union of the value types without the never
s.
Which would be used like:
type VehiclesThatFly = VehiclesSubset<{
[Vehicles.CAR]: false
[Vehicles.BOAT]: false
[Vehicles.AIRPLANE]: true
}>
const airplaneCanFly: VehiclesThatFly = Vehicles.AIRPLANE
const carCantFly: VehiclesThatFly = Vehicles.CAR // error
And this constraint here:
Record<Vehicles, boolean>
Requires an entry for each key of Vehicles
. So if you add an enum entry, you should get type errors:
enum Vehicles {
CAR = 1,
BOAT = 2,
AIRPLANE = 3,
MOTORCYCLE = 4,
}
type VehiclesThatFly = VehiclesSubset<{
[Vehicles.CAR]: false
[Vehicles.BOAT]: false
[Vehicles.AIRPLANE]: true
}>
// Type '{ 1: false; 2: false; 3: true; }' does not satisfy the constraint 'Record<Vehicles, boolean>'.
// Property '4' is missing in type '{ 1: false; 2: false; 3: true; }' but required in type 'Record<Vehicles, boolean>'.(2344)
Upvotes: 1