我们应该使用 ! (非空断言)或?。(可选链接)在 TypeScript 中?

pri*_*rum 6 javascript typescript reactjs

我正在学习 TypeScript 和 React,当我遇到这段代码时,我发现!(非空断言)和?.(可选链接)都可以使用。

import { FC, FormEvent, useRef } from "react";

const NewTodo: FC = () => {
  const textInputRef = useRef<HTMLInputElement>(null);

  function todoSubmitHandler(ev: FormEvent) {
    ev.preventDefault();
    //                               v here v
    const enteredText = textInputRef.current!?.value;
    console.log(enteredText);
  }

  return (
    <form onSubmit={todoSubmitHandler}>
      <div>
        <label htmlFor="todo-text">Todo Text</label>
        <input type="text" id="todo-text" ref={textInputRef} />
      </div>
      <button type="submit">ADD TODO</button>
    </form>
  );
};

export default NewTodo;
Run Code Online (Sandbox Code Playgroud)

我所知道的!是,它告诉 Typescript 这个值永远null不是 Nor undefined。另一方面,?.用于防止未找到属性时出现错误,而是返回undefined。在上面的示例中,我可以使用其中一个!?.什至两者结合使用!?.,并且 Typescript 编译器不会抱怨。那么哪一种最好、最安全使用呢?

jca*_*alz 12

可选链接比?.非空断言使用起来更安全!

考虑以下接口:

interface Foo {
    bar?: {
        baz: string;
    }
}
Run Code Online (Sandbox Code Playgroud)

bar属性是可选的。如果它不存在,那么undefined当您阅读它时就会存在。如果它确实存在,它将具有baztype 属性string。如果您只是尝试访问该baz属性而不确保bar已定义该属性,您将收到编译器错误,警告您可能存在运行时错误

function oops(foo: Foo) {
    console.log(foo.bar.baz.toUpperCase()); // compiler error
    // -------> ~~~~~~~
    // Object is possibly undefined
}
Run Code Online (Sandbox Code Playgroud)

可选链接在运行时具有实际效果,并且如果您尝试访问的属性不存在,则会短路到某个值。undefined如果您不确定某个属性是否存在,可选链接可以保护您免受某些运行时错误的影响。TypeScript 编译器不会抱怨以下代码,因为它知道您正在做的事情现在是安全的:

function optChain(foo: Foo) {
    console.log(foo.bar?.baz.toUpperCase());
}

optChain({ bar: { baz: "hello" } }); // HELLO
optChain({}); // undefined
Run Code Online (Sandbox Code Playgroud)

如果您不确定属性访问是否安全并且希望运行时检查来保护您,则应该使用可选链。


另一方面,非空断言在运行时没有任何影响。这是您告诉编译器的一种方式,即使它无法验证某个属性是否存在,您也可以断言这样做是安全的。这还具有阻止编译器抱怨的效果,但您现在已经接管了确保类型安全的工作。如果在运行时,您断言定义的值实际上是未定义的,那么您就对编译器撒了谎,并且可能会遇到运行时错误:

function nonNullAssert(foo: Foo) {
    console.log(foo.bar!.baz.toUpperCase());
}

nonNullAssert({ bar: { baz: "hello" } }); // HELLO
nonNullAssert({}); //  TypeError: foo.bar is undefined
Run Code Online (Sandbox Code Playgroud)

仅当您确定属性访问是安全的并且希望方便地跳过运行时检查时,才应使用非空断言。


Playground 代码链接