• TypeScript报错:Object is possibly “null“ 解决方法——断言函数


    学习一些博客的笔记。
    原文:Assertion Functions in TypeScript — Marius Schulz
    TS系列:TypeScript Evolution — Marius Schulz

    如果很急,可以直接看 文章目录:省流结论。

    断言函数是一种对类型系统的支持。

    TypeScript 3.7 implemented support for assertion functions in the type system.

    举个例子,我们想拿到一个id为root的节点,并给它添加点击事件:

    const root = document.getElementById("root");
    
    root.addEventListener("click", e => {
      /* ... */
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此时TS会报错Object is possibly null。因为root是HTMLElement | null,而null是没有办法添加点击事件的。因此,我们在添加点击事件前需要保证root是非空 null非未定义 undefined的。

    我们有3种方法。

    方法1:使用非空断言运算符!

    非空断言运算符!,告诉TS假定root是非空非未定义。

    const root = document.getElementById("root")!;
    
    root.addEventListener("click", e => {
      /* ... */
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    root原本的类型是HTMLElement | null,使用了!后忽视了null,TS会只把他当作HTMLElement

    然而, 使用非空断言!并不是这种情况的正确解决方法 。原因是:!运算符编译成JS后会自动消失,TS代码不会去判断root是否是null。此时若root是null,还是会报错(没法添加点击事件)。

    方法2:内联空检查

    先判断root是否为空。

    const root = document.getElementById("root");
    
    // 这里的root是 HTMLElement | null
    
    if (root === null) {
      throw Error("Unable to find DOM element #root");
    }
    
    // 这里的root是 HTMLElement
    
    root.addEventListener("click", e => {
      /* ... */
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这种方法不包含任何特定于TypeScript的语法;以上所有都是语法上有效的JavaScript。

    方法3:实现断言函数

    如果value是null或undefined,就抛出异常。

    function assertNonNullish(
      value: unknown,
      message: string
    ) {
      if (value === null || value === undefined) {
        throw Error(message);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    但是:

    const root = document.getElementById("root");
    
    // Type: HTMLElement | null
    root;
    
    assertNonNullish(root, "Unable to find DOM element #root");
    
    // Type: HTMLElement | null
    root;
    
    // 还是报错:TS认为这里的root还是有可能是null
    root.addEventListener("click", e => {
      /* ... */
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    TS并不知道我们的断言函数assertNonNullish已经过滤掉了root是null或undefined的情况。我们需要 显式地 让TS知道此函数assertNonNullish是断言函数,它断言的值是非null/undefined的。我们可以在返回类型中写关键字:

    function assertNonNullish<TValue>(
      value: TValue,
      message: string
    ): asserts value is NonNullable<TValue> {
      if (value === null || value === undefined) {
        throw Error(message);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    其中,asserts value is NonNullable 是一个断言签名,它表示:如果函数正常返回(即,没有抛出错误),则value参数的类型是非空的(这里的是泛型)。TS可以用此信息来缩小传递给value参数的表达式的类型。

    缩小类型了:

    const root = document.getElementById("root");
    
    // Type: HTMLElement | null
    root;
    
    assertNonNullish(root, "Unable to find DOM element #root");
    
    // Type: HTMLElement
    root;
    
    root.addEventListener("click", e => {
      /* ... */
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    NonNullable

    是一种条件类型:

    /**
     * Exclude null and undefined from T
     */
    type NonNullable<T> = T extends null | undefined ? never : T;
    
    • 1
    • 2
    • 3
    • 4

    把T中的null和undefined去掉了。

    如:

    • NonNullableHTMLElement
    • NonNullableHTMLElement
    • NonNullablenever

    省流结论

    function assertNonNullish<TValue>(
      value: TValue,
      message: string
    ): asserts value is NonNullable<TValue> {
      if (value === null || value === undefined) {
        throw Error(message);
      }
    }
    
    const root = document.getElementById("root");
    assertNonNullish(root, "Unable to find DOM element #root");
    
    root.addEventListener("click", e => {
      /* ... */
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    springboot项目创建5种方式
    dubbo-demo及用jmeter测试
    tRNA甲基化偶联3-甲基胞嘧啶(m3C)|tRNA-m3C (3-methylcy- tidine)
    【微服务35】分布式事务Seata源码解析三:从Spring Boot特性来看Seata Client 启动时都做了什么【云原生】
    电源线虚接,导致信号线发烫
    更好的用户体验, 开源实时监控新版发布
    永不过时的优雅 KOREANO ESSENTIAL 2022秋冬系列全新上市
    Windows使用scp上传文件到linux服务器
    C++变量声明和变量定义
    阿晨的厨艺
  • 原文地址:https://blog.csdn.net/karshey/article/details/134265992