• 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
  • 相关阅读:
    【图像分类】【深度学习】【轻量级网络】【Pytorch版本】MobileNets_V2模型算法详解
    java-数据迁移-定制拓展
    分销小程序开发|分销小程序要怎么吸引客户?
    基于若依ruoyi-nbcio支持flowable流程分类里增加流程应用类型
    Linux 编译安装中的 configure 命令
    传输层 TCP连接管理 优化关闭连接时的TIME-WAIT状态
    网站一键灰色
    数据的IO和复用
    如何为WPF应用程序制作一个虚拟键盘?这里有答案(Part 1)
    NLP:《ChatGPT: Optimizing Language Models for Dialogue一种优化的对话语言模型》翻译与解读
  • 原文地址:https://blog.csdn.net/karshey/article/details/134265992