Reputation: 7965
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!
Upvotes: 3
Views: 235
Reputation: 4149
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.
Upvotes: 3