在函数末尾分配(或断言)never
是一种在Typescript中使用的技术,以便在编译时强制进行穷举检查。
然而,要使编译器检测到这一点,它需要检查显式字符串,以确定函数在never
的赋值/断言之前是否明确返回。
是否有可能引入某种类型的Object.freeze变体,它只适用于对象文字,并进一步向上链,以便可以完成以下内容?
更好的是,有没有办法创建一个界面,其中的键自动是每个的Action.type(在本例中)?如果是这种情况 - actionMap
可以简单地声明为该接口,这将强制在编译时进行检查。
两者都是同一问题的解决方案......只给出一个有区别的联合,是否可以在编译时进行这样的详尽检查,而不需要在函数中使用显式字符串?
interface Increment {
type: 'increment'
}
interface Decrement {
type: 'decrement'
}
type Action = Increment | Decrement
const inc: Increment = { type: 'increment' };
const dec: Decrement = { type: 'decrement' };
//this would be a typescript variation
const actionMap = Object.freeze({
[inc.type]: n => n + 1,
[dec.type]: n => n-1
});
function doAction(action: Action, val: number): number {
if(actionMap[action.type]) {
return actionMap[action.type](val);
}
//this would error at compile time if the above checked failed
const _exhaustiveCheck: never = action;
}
console.log(doAction(inc, 1));
console.log(doAction(dec, 1));
答案 0 :(得分:3)
有一种相当直接的方法来制作一张地图,以确保它在一个有区别的联合中具有每个案例的值。您只需设置它以使其键的类型是区分的联合标识符。
type ActionMap = {
[P in Action["type"]]: (val:number)=>number
};
然后,您可以实现此界面,如下所示:
var map: ActionMap = {
decrement: n => n - 1,
increment: n=> n + 1
}
编辑:在一堆乱七八糟的东西之后,我发现了一个更通用,功能更强大的解决方案,它不仅可以输入区分联合值的键,还可以输入有效负载。
首先:以key:type对的形式定义你的联合。 (我认为无论如何这都是更清晰的)
type Actions = {
"increment": { incrementValue: number }
"decrement": { decrementValue: number }
}
第二个:从该地图创建一个Action Disriminated Union。这不是世界上最清晰的代码,它为ActionsMap
中的每个键值对做了什么,通过添加类型值{type:key}
来创建新类型然后将所有这些类型相加以创建您的歧视联盟。
type Action = {
[P in keyof Actions]: { type: P } & ActionsMap[P]
}[keyof Actions];
第三次:为地图创建一个类型
type ActionsMap = {
[P in keyof Actions]: (val:number,action:Actions[P])=>number
}
Forth :享受完全类型安全的动作/减速器地图!
const map:ActionsMap = {
decrement: (val, action) => val + action.decrementValue,
increment: (val, action) => val + action.incrementValue,
}
公平警告。这极大地限制了打字稿定义可以做什么,我个人已经被依赖于一些打字稿边缘行为而被咬,只是为了让它在下一个版本中被改变。