首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >TypeScript中的类型安全谓词函数

TypeScript中的类型安全谓词函数
EN

Stack Overflow用户
提问于 2019-02-21 07:50:40
回答 2查看 5.7K关注 0票数 9

我的目标是在isNull中编写谓词函数(例如,isUndefined和TypeScript ),满足以下条件:

  1. 可以单独使用:array.filter(isNull)
  2. 可以逻辑组合:array.filter(and(not(isNull), not(isUndefined)))
  3. 使用类型保护,以便TypeScript知道例如,array.filter(isNull)的返回类型将是null[]
  4. 组合谓词可以在不中断类型推断的情况下提取到新的谓词函数中:const isNotNull = not(isNull)

前两个条件很容易实现:

代码语言:javascript
复制
type Predicate = (i: any) => boolean;

const and = (p1: Predicate, p2: Predicate) =>
    (i: any) => p1(i) && p2(i);

const or = (p1: Predicate, p2: Predicate) =>
    (i: any) => p1(i) || p2(i);

const not = (p: Predicate) =>
    (i: any) => !p(i);

const isNull = (i: any) =>
    i === null;

const isUndefined = (i: any) =>
    i === undefined;

const items = [ "foo", null, 123, undefined, true ];
const filtered = items.filter(and(not(isNull), not(isUndefined)));
console.log(filtered);

但是,由于这里没有使用类型保护,TypeScript假设变量filtereditems具有相同的类型,即(string,number,boolean,null,undefined)[],而现在它实际上应该是(string,number,boolean)[]

所以我添加了一些打字脚本的魔法:

代码语言:javascript
复制
type Diff<T, U> = T extends U ? never : T;

type Predicate<I, O extends I> = (i: I) => i is O;

const and = <I, O1 extends I, O2 extends I>(p1: Predicate<I, O1>, p2: Predicate<I, O2>) =>
    (i: I): i is (O1 & O2) => p1(i) && p2(i);

const or = <I, O1 extends I, O2 extends I>(p1: Predicate<I, O1>, p2: Predicate<I, O2>) =>
    (i: I): i is (O1 | O2) => p1(i) || p2(i);

const not = <I, O extends I>(p: Predicate<I, O>) =>
    (i: I): i is (Diff<I, O>) => !p(i);

const isNull = <I>(i: I | null): i is null =>
    i === null;

const isUndefined = <I>(i: I | undefined): i is undefined =>
    i === undefined;

现在看来,filtered被正确地简化为(string,number,boolean)[]类型。

但是,由于not(isNull)可能经常被使用,所以我希望将其提取到一个新的谓词函数中:

代码语言:javascript
复制
const isNotNull = not(isNull);

虽然这在运行时非常有效,但不幸的是它没有编译(启用严格模式的TypeScript 3.3.3 ):

代码语言:javascript
复制
Argument of type '<I>(i: I | null) => i is null' is not assignable to parameter of type 'Predicate<{}, {}>'.
  Type predicate 'i is null' is not assignable to 'i is {}'.
    Type 'null' is not assignable to type '{}'.ts(2345)

因此,当使用谓词作为数组的参数时,filter方法TypeScript可以从数组中推断出I的类型,但是当将谓词提取到单独的函数中时,这就不再起作用了,TypeScript回到了基本对象类型{},它破坏了一切。

有办法解决这个问题吗?说服TypeScript坚持泛型类型I而不是在定义isNotNull函数时将其解析为{}的一些技巧?或者这是TypeScript的一个限制,目前无法完成?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-07-19 19:16:27

刚刚在这里找到了我自己两年的问题,然后用最新的TypeScript版本(4.3.5)再试一次,这个问题就不再存在了。以下代码编译得很好,并且正确地推断了类型:

代码语言:javascript
复制
type Diff<T, U> = T extends U ? never : T;

type Predicate<I, O extends I> = (i: I) => i is O;

const and = <I, O1 extends I, O2 extends I>(p1: Predicate<I, O1>, p2: Predicate<I, O2>) =>
    (i: I): i is (O1 & O2) => p1(i) && p2(i);

const or = <I, O1 extends I, O2 extends I>(p1: Predicate<I, O1>, p2: Predicate<I, O2>) =>
    (i: I): i is (O1 | O2) => p1(i) || p2(i);

const not = <I, O extends I>(p: Predicate<I, O>) =>
    (i: I): i is (Diff<I, O>) => !p(i);

const isNull = <I>(i: I | null): i is null =>
    i === null;

const isUndefined = <I>(i: I | undefined): i is undefined =>
    i === undefined;

const isNotNull = not(isNull);
const isNotUndefined = not(isUndefined);

const items = [ "foo", null, 123, undefined, true ];
const filtered = items.filter(and(isNotNull, isNotUndefined));
console.log(filtered);
票数 12
EN

Stack Overflow用户

发布于 2019-02-22 10:23:30

从上下文传递类型信息。这段代码编译得很好

代码语言:javascript
复制
// c: (string | number)[]
let c = [1, 2, 'b', 'a', null].filter(not<number | string | null, null>(isNull)); 
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54801835

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档