从以下联合类型:
type Modifier =
| Date
| RangeModifier
| BeforeModifier
| AfterModifier
| BeforeAfterModifier
| DaysOfWeekModifier
| FunctionModifier
| undefined;
…我用类型名构建了如下代码:
const MODIFIER_NAMES = [
'undefined',
'Date',
'RangeModifier',
'BeforeModifier',
'AfterModifier',
'BeforeAfterModifier',
'DaysOfWeekModifier',
'FunctionModifier',
] as const;
type ModifierNamesTuple = typeof MODIFIER_NAMES;
type ModifierNames = ModifierNamesTuple[ number ];
我需要严格地将ModifierNames
中的名称映射到Modifier
中相应的类型,以便我可以在JS map对象中使用它们。类似…
const modifierMap = new Map<ModifierNames, Modifier>();
…但是在键和它的值之间具有预期的类型安全。例如:
// For these modifiers (notice their type)...
const rangeModifier: Modifier = {
from: new Date(),
to: new Date()
}
const beforeModifier: Modifier = {
before: new Date()
}
// The following should be invalid
modifierMap.set('BeforeModifier', rangeModifier);
// While the following should be valid
modifierMap.set('RangeModifier', rangeModifier);
我怎么能做到这一点?
type FunctionModifier = {
tag: 'FunctionModifier'
}
type DaysOfWeekModifier = {
tag: 'DaysOfWeekModifier'
}
type Dictionary = {
'Date': Date,
'DaysOfWeekModifier': DaysOfWeekModifier,
'FunctionModifier': FunctionModifier,
'undefined': undefined
}
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Values<T> = T[keyof T]
/**
* First step
*/
type HashMap =
{
[Prop in keyof Dictionary]: Map<Prop, Dictionary[Prop]>
}
/**
* Second step
*/
type UnionOfStates = Values<HashMap>
/**
* Third step
*/
type MapOverloading = UnionToIntersection<UnionOfStates>
const modifierMap: MapOverloading = new Map();
modifierMap.get('FunctionModifier') // FunctionModifier | undefined
modifierMap.set('DaysOfWeekModifier', { tag: 'DaysOfWeekModifier' }) // ok
modifierMap.set('DaysOfWeekModifier', { tag: 'invalid' }) // error
modifierMap.set('DaysOfWeekModifier', 42) // error
游乐场
为了使它工作,你需要过载modifierMap
。我的意思是,你需要创建一个所有可能的Map
状态的并集然后让它们相交
如果你想在函数内部调用map.set
,你需要推断函数参数
type FunctionModifier = {
tag: 'FunctionModifier'
}
type DaysOfWeekModifier = {
tag: 'DaysOfWeekModifier'
}
type Dictionary = {
'Date': Date,
'DaysOfWeekModifier': DaysOfWeekModifier,
'FunctionModifier': FunctionModifier,
'undefined': undefined
}
type Values<T> = T[keyof T]
const modifierMap = new Map<keyof Dictionary, Values<Dictionary>>();
const setter = <Key extends keyof Dictionary>(key: Key, value: Dictionary[Key]) => {
modifierMap.set(key, value);
}
setter('Date', new Date) // ok
setter('Date', 32)// error