Reputation: 7066
I have a function that, given it knows about the mapping between a key and a type, should return the correct type when called with the matching key.
Below is a simplified example of my use case. I'm almost there but I'm missing something.
Note: this is for a library and we don't know the types as users will provide them.
type Plane = {
name: string;
wings: number;
};
type Car = {
name: string;
wheels: number;
};
type Vehicles = {
cars: Car;
planes: Plane;
};
type TypeMap = { [key: string]: any };
type Mapper<VehicleMapType extends TypeMap = TypeMap> = {
getVehicle<
VehicleType extends
| Extract<keyof VehicleMapType, string>
| never = Extract<keyof VehicleMapType, string>
>(
type: VehicleType
): VehicleMapType[VehicleType];
};
const mapper: Mapper<Vehicles> = null;
const vehicle1 = mapper.getVehicle('cars');
// I have a Car!
console.log(vehicle1);
const getVehicles = <
VehicleMapType extends TypeMap = TypeMap,
VehicleType extends Extract<keyof VehicleMapType, string> | never = Extract<
keyof VehicleMapType,
string
>
>(
type: VehicleType
): VehicleMapType[VehicleType] => {
return null;
};
const vehicle2 = getVehicles<Vehicles>('cars');
// How can I get a Car instead of a Car | Plane?
console.log(vehicle2);
Thanks!
Edit
Besides, we also face an issue in the Mapper implementation as you can see in this TS Playground
Upvotes: 1
Views: 523
Reputation: 33739
You either have to
Vechicles
), ortype StringKeys = Record<string, any>;
type Named = { name: string };
type Plane = Named & { wings: number };
type Car = Named & { wheels: number };
type Vehicles = {
cars: Car;
planes: Plane;
};
type Mapper<T extends StringKeys> = {
getVehicle<K extends keyof T>(type: K): T[K];
};
const mapper: Mapper<Vehicles> = {
getVehicle (type) {
const vechicles: Vehicles = {
cars: {name: 'a car', wheels: 4},
planes: {name: 'a plane', wings: 2},
};
const result = vechicles[type];
if (result) return result;
throw new Error('unknown vehicle type');
}
};
const vehicle1 = mapper.getVehicle('cars'); // Car
// Generic functional abstraction:
const pickFrom = <T extends StringKeys>(o: T): <K extends keyof T>(key: K) => T[K] => {
return key => o[key];
};
declare const vehicles: Vehicles;
const getVehicles = pickFrom(vehicles);
const vehicle2 = getVehicles('cars'); // Car
Upvotes: 2