Imagine having a structure like this:
type TInfoGeneric<TType extends string, TValue> = {
valueType: TType,
value: TValue, // Correspond to valueType
}
To prevent redundancy, a type map is created to list the potential valueType
and associate it with the corresponding value's type.
type TInfoTypeMap = {
num: number;
str: string;
}
To construct TInfo
, mapped types are utilized to map all types into TInfoGeneric
and extract only the value side.
type TAllPossibleTInfoMap = {
[P in keyof TInfoTypeMap]: TInfoGeneric<P, TInfoTypeMap[P]>;
};
type TInfo = TAllPossibleTInfoMap[keyof TAllPossibleTInfoMap]; // TInfoGeneric<"num", number> | TInfoGeneric<"str", string>
Subsequently, a separate mapped type is created solely for handlers to define handlers for all types.
type TInfoHandler = {
[P in keyof TInfoTypeMap]: (value: TInfoTypeMap[P]) => any
};
const handlers: TInfoHandler = {
num: (value) => console.log(value.toString(16)),
str: (value) => console.log(value),
}
Finally, an approach to utilize the handler is devised through a function like the following:
function handleInfo(info: TInfo) {
handlers[info.valueType](info.value); // Error
}
An error is encountered:
Argument of type 'string | number' is not assignable to parameter of type 'number & string'.
Type 'string' is not assignable to type 'number & string'.
Type 'string' is not assignable to type 'number'.
The situation is clear when info.valueType
is either 'num' or 'str'. However, is there a way to refine the code to pass TypeScript type-checking?