In TypeScript (v4.5.4), I am trying to define an object type via an index signature. I want TypeScript to enforce certain sub-properties in the object to have matching types, but those types are allowed to vary between top-level properties.
In the below (non-working) example, I want all happy drivers to drive their favorite car type. A mismatch between a driver's favorite car type and the actual type of their car should cause a TypeScript compiler error.
type CarType = 'Minivan' | 'Sports car' | 'Sedan' ; // | 'Pickup truck' |, etc. Imagine this union type has many possible options, not just three.
type Car = {
carType: CarType
// A car probably has many additional properties, not just its type, but those are left out of this minimal example.
// The solution should be resistant to adding additional properties on `Car` (and the `Driver` type below).
};
type Driver = {
favoriteCarType: CarType
car: Car
};
/**
* Happy drivers drive their favorite car type.
*/
const happyDrivers: { [name: string]: Driver } = {
alice: {
favoriteCarType: 'Minivan',
car: {
carType: 'Minivan', // ✅ Alice drives her favorite type of car.
},
},
bob: {
favoriteCarType: 'Sports car',
car: {
carType: 'Sedan', /* ❌ Bob doesn't drive his favorite type of car!
This currently does not throw a compiler error because my types are too permissive, but I want it to. */
},
},
};
I've tried applying generics to the index signature and/or the Car
and/or Driver
type in all the ways I could think of, but I could not get the compiler to enforce the constraint that a driver's favoriteCarType
must exactly match their car
's carType
.
Can you help me out?