问题描述
游乐场
You can find a playground with the code here
给出以下类型定义:
type State = ["IDLE"] | ["STARTING_NEW_CONVERSATION"] | ["CHANNEL_ACTIVE","SYNCED" | "EXPLORING"];
const state: State = ...
以及以下辅助方法:
export function matches<T extends [string] | [string,string],X = T[0],Y = T[1]>(
value: T,check: T[0],check2?: Extract<T,[X,Y]>[1]
) {
return value[0] === check && (check2 ? value[1] === check2 : true);
}
// another variation...
export function matches<T extends [string] | [string,string]>(
value: T,check2?: T extends [infer X,infer Y] ? Extract<T,Y]>[1] : never
) {
return value[0] === check && (check2 ? value[1] === check2 : true);
}
主要有效-但是,我可以这样称呼它
matches(state,"IDLE","EXPLORING")
对于编译器来说这是可以的-我希望第二个参数的类型可以解析为never
或undefined
。
- 值
["IDLE","EXPLORING"]
无法分配给state
- 正确书写
Extract<State,["IDLE","EXPLORING"]>
会提取never
所以我的问题是
为什么用表示无效类型的值成功调用“ matches”?
解决方法
Typescript不会根据您在第二个参数中传递的值来缩小T
的类型。
给出以下代码:
type State = ["IDLE"] | ["STARTING_NEW_CONVERSATION"] | ["CHANNEL_ACTIVE","SYNCED" | "EXPLORING"];
const state: State = {} as any
export function matches<T extends [string] | [string,string],X = T[0],Y = T[1]>(
value: T,check: T[0],check2?: Extract<T,[X,Y]>[1]
) {
return value[0] === check && (check2 ? value[1] === check2 : true);
}
matches(state,"IDLE","EXPLORING")
Typescript为参数推断以下类型:
value: State
check: "IDLE" | "STARTING_NEW_CONVERSATION" | "CHANNEL_ACTIVE"
check2: "SYNCED" | "EXPLORING"
当您传递"IDLE"
作为第二个参数时,TS只会看它并说:“是的,它匹配"IDLE" | "STARTING_NEW_CONVERSATION" | "CHANNEL_ACTIVE"
并继续。它不会缩小任何范围。
如果您需要基于第二个参数和第三个参数的类型相互限制,则应在模板参数列表中放置一个要约束的通用类型,并从两个参数中引用它:
function matches<T extends State,X extends T[0],Y extends T[1]>(
value: T,check: X,Y]>[1],) {
return value[0] === check && (check2 ? value[1] === check2 : true);
}
type State = ["IDLE"] | ["STARTING_NEW_CONVERSATION"] | ["CHANNEL_ACTIVE","SYNCED" | "EXPLORING"];
const state: State = {} as any
matches(state,'IDLE',) // WORKS
matches(state,'CHANNEL_ACTIVE','EXPLORING') // Works
matches(state,'EXPLORING') // ERROR: Argument of type '"EXPLORING"' is not assignable to parameter of type 'undefined'.
主要区别在于我们在模板列表中定义类型X
,然后在参数约束的 both 中使用它。这迫使TS推断满足两个参数的单个类型X
。否则,您的参数约束将不相关。
(编辑:已更新为简化的解决方案,与您的原始代码非常接近)