I am struggling to develop strong typing for a particular function written in TypeScript...
For illustrative purposes, lets say I work at the Department of Motor Vehicles and have a function registerVehicles
that can accept registration details for many vehicles organised by name, and returns the license plates for each vehicle.
interface RegistrationDetails {
transmission?: 'manual' | 'automatic';
wheelCount?: number;
horsePower?: number;
}
const licensePlates = registerVehicles({
sportsCar: {
transmission: 'manual',
wheelCount: 4,
horsePower: 762
},
motorBike: {
transmission: 'manual',
wheelCount: 2
},
hoverBoard: {
// All registration details are optional
}
});
The function returns an object with the names of each vehicle and their newly registered license plates:
expect(licensePlates).to.eql({
sportsCar: 'ABCD-1234',
motorBike: 'SPDY-5678',
hoverBoard: 'BTTF-2'
});
The function exists and works perfectly, the problem is getting it strongly typed.
The variable licensePlates
should be implicitly typed from the result of the function.
Trying to pass a registration detail that doesn't exist should error at compile time.
registerVehicles({
car: {
cowPower: 500 // <-- Spelling mistake, should be horsePower
}
})
Trying to access the license plate of a vehicle you didn't register should error at compile time:
const licensePlates = registerVehicles({
ferrari: {
horsePower: 562
}
});
alert(licensePlates.furrari); // <-- Spelling mistake, should be ferrari
TypeScript should know each license plate is a string at compile-time
const licensePlates = registerVehicles({
ferrari: {}
});
alert(licensePlates.ferrari * 5); // <-- Error, you can't multiple a string
I've gotten close, but every solution I try ultimately fails to meet at least one of the above requirements. Help me Stack Overflow community, you're my only hope!
The utility types "Record" does what you want. With it you can map dynamic properties from one type to another type. (Try it in the Playground):
function registerVehicles<K extends string>(p: Record<K, RegistrationDetails> ): Record<K, string> {
return null;
}
The K
type will be a String Literal Types eg. "sportsCar" | "motorBike" | "hoverBoard"
.
Update: It is not well documented. But here is a link to the Documentation and I found a example here.