soe*_*165 11 generics narrowing typescript type-narrowing
我想在一个函数中链接 2 个泛型类型,并通过检查其中之一来对这两种类型使用缩小范围。这样做的正确方法是什么?
type A = 'A';
type B = 'B';
type AB = A | B
type ComplexType<T> = {value: T}
const f = (next: ComplexType<A>) => {}
const builder = <T extends AB>(value: T) => (next: ComplexType<T>) => {
if (value === 'A') {
f(next) // expect next is ComplexType<A> but got error
}
}
Run Code Online (Sandbox Code Playgroud)
jca*_*alz 10
目前没有办法通过检查诸如之类的值来缩小类型参数的范围。这可能是真的,但这并不意味着是真的。毕竟, Maybe的类型为,例如通过传入一个表达式,其中编译器推断出完整的联合类型:T
value
value === "A"
T
"A"
value
"A" | "B"
builder(Math.random() <= 0.999 ? "A" : "B") // no error
Run Code Online (Sandbox Code Playgroud)
这里有 99.9% 的可能性你已经通过了"A"
,但是你仍然有可能已经通过了"B"
。编译器推断T
为"A" | "B"
. 因此next
参数的类型为ComplexType<"A" | "B">
。因此,当您调用此命令时,不会出现编译器错误:
builder(Math.random() <= 0.999 ? "A" : "B")({ value: "B" }); // no error
Run Code Online (Sandbox Code Playgroud)
这意味着编译器在技术上是正确的,但f(next)
可能存在错误。
GitHub 中存在多个现有问题,要求支持缩小泛型函数体内的类型参数。与您的代码最相关的可能是microsoft/TypeScript#27808。这需要某种方法来告诉编译器T
应该是or "A"
和 "B"
not "A" | "B"
。也许语法会是类似T extends_oneof [A, B]
或(T extends A) | (T extends B)
完全不同的东西。然后,当您测试时,value === "A"
编译器可能会得出这样的结论T extends A
: ,一切都会正常。唉,目前还没有这样的支持。
现在你只需要解决它。如果您相当有信心没有人会builder()
错误地调用您的函数,您可以使用类型断言并继续:
const builder = <T extends AB>(value: T) => (next: ComplexType<T>) => {
if (value === 'A') {
f(next as ComplexType<A>) // okay
}
}
Run Code Online (Sandbox Code Playgroud)
如果您确实需要防止调用者做错误的事情,builder()
您可以制作越来越复杂的调用签名,相当于模拟“扩展其中之一”约束,例如:
type NoneOf<T, U extends any[]> =
[T] extends [U[number]] ? never : T;
type OneOf<T, U extends any[]> =
U extends [infer F, ...infer R] ? [T] extends [F] ? NoneOf<T, R> : OneOf<T, R> : never;
const builder = <T extends "A" | "B">(
value: T & OneOf<T, ["A", "B"]>
) => (next: ComplexType<T>) => {
if (value === 'A') {
f(next as ComplexType<"A">)
}
}
builder(Math.random() <= 0.999 ? "A" : "B"); // error now
builder2("A") // okay
builder2("B") // okay
Run Code Online (Sandbox Code Playgroud)
但当然编译器无论如何都无法在主体内遵循这一点builder
(通用条件类型很难处理),因此您仍然需要类型断言。就我个人而言,我只会将原始签名与类型断言一起使用,并且只有在实践中遇到无效调用时才重新访问更复杂的内容。
归档时间: |
|
查看次数: |
7648 次 |
最近记录: |