组合类型保护签名



我按照这里解释的示例创建了一个Or函数,该函数接受多个类型保护,并为并集类型返回一个类型保护。例如:CCD_ 2。但是,我不能将返回的函数用作Array.filter的参数。

定义:

type TypeGuard<A, B extends A> = (a: A) => a is B;
type GuardType<T> = T extends (o: any) => o is infer U ? U : never
class A { q: any }
class B { p: any }
declare function isA(x: any): x is A
declare function isB(x: any): x is B
function Or<T extends TypeGuard<any, any>>(guards: T[]): T extends TypeGuard<infer A, any> ? (a: A) => a is GuardType<T> : never;
function Or<T extends TypeGuard<any, any>>(guards: T[]) {
return function (arg: T) {
return guards.some(function (predicate) {
predicate(arg);
});
}
}

代码示例:

let isAOrB = Or([isA, isB]); // inferred as ((a: any) => a is A) | ((a: any) => a is B)
let isOr_value = isAOrB({ q: 'a' }); // here isAOrB is inferred as (a: any) => a is A | B which is what I want
[{}].filter(isAOrB).forEach(x => { }); // here I expected x's type to be inferred as A | B because of the type guard, however filter's overload is the regular one, returning the same {}[] type as the source array

我知道我可以显式地编写一个lambda表达式作为filter参数来强制进行类型推断:

[{}].filter((x): x is A | B => isAOrB(x)).forEach(x => { });

但这正是我想要避免的。

与And函数组合型防护相同的问题

我使用了这里显示的UnionToIntersection构造,但无法正确键入And函数,这是我的尝试和得到的错误:

function And<T extends TypeGuard<any, any>>(guards: T[]): T extends TypeGuard<infer A, any> ? (a: A) => a is UnionToIntersection<GuardType<T>> : never;
// the above gives error A type predicate's type must be assignable to its parameter's type.
Type 'UnionToIntersection<GuardType<T>>' is not assignable to type 'A'.
Type 'unknown' is not assignable to type 'A'.

这里的问题似乎是您意外地分发了条件类型。如果T是一个空类型参数,那么条件类型T extends Foo ? Bar : Baz最终会将T分解为其联合成员,为每个成员评估条件,并将结果重新联合在一起。这会给您带来不想要的((a: any) => a is A) | ((a: any) => a is B)

关闭这种分布的最简单方法是将裸类型参数"挂"在一个元素元组中,如[T] extends [Foo] ? Bar : Baz:

function Or<T extends TypeGuard<any, any>>(guards: T[]): 
[T] extends [TypeGuard<infer A, any>] ? (a: A) => a is GuardType<T> : never;

这应该会给你带来你想要的行为:

[{}].filter(isAOrB).forEach(x => { }); // x is A | B

同样的事情也适用于您的And,只是编译器不会理解UnionToIntersection<...>将是类型保护函数的有效窄化,所以您可能希望像这样将其封装在Extract<>中:

declare function And<T extends TypeGuard<any, any>>(guards: T[]):
[T] extends [TypeGuard<infer A, any>] ? 
(a: A) => a is Extract<UnionToIntersection<GuardType<T>>, A> : never;
let isAAndB = And([isA, isB]); 
let isAnd_value = isAAndB({ q: 'a' }); 
[{}].filter(isAAndB).forEach(x => { }); // A & B

现在我觉得不错。好吧,希望能有所帮助;祝你好运

游乐场链接到代码

最新更新