javascript 我们应该在TypeScript中使用!(非空Assert)还是?.(可选链接)?

mrwjdhj3  于 2023-03-11  发布在  Java
关注(0)|答案(2)|浏览(132)

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

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;

我对!的了解是,它告诉Typescript这个值既不是null也不是undefined。另一方面,?.用于防止在找不到属性时出错,而是返回undefined。在上面的示例中,我可以使用!?.,甚至可以将两者组合使用!?.。而Typescript编译器也不会抱怨。那么使用哪一种是最好和最安全的呢?

uxh89sit

uxh89sit1#

可选链接?.比非空Assert!使用起来 * 更安全 *。
考虑以下接口:

interface Foo {
    bar?: {
        baz: string;
    }
}

bar属性是可选的。如果它不存在,则读取时为undefined。如果它存在,则具有string类型的baz属性。如果您只是尝试访问baz属性,而没有确保定义bar,则会收到 * 编译器错误 *,警告您可能存在 * 运行时错误 *:

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

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

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

optChain({ bar: { baz: "hello" } }); // HELLO
optChain({}); // undefined

如果您不确定属性访问是否安全,并且希望运行时检查能够保护您,则应使用可选链接。
另一方面,非空Assert在运行时没有任何作用。这是一种告诉编译器即使它不能验证属性存在的方法,但你是在“Assert”这样做是安全的。这也有阻止编译器抱怨的效果,但你现在已经接管了确保类型安全的工作。如果在运行时,你Assert的定义值实际上是未定义的,那么你对编译器撒谎了,你可能会遇到一个运行时错误:

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

nonNullAssert({ bar: { baz: "hello" } }); // HELLO
nonNullAssert({}); // 💥 TypeError: foo.bar is undefined

只有当你确信你的属性访问是安全的,并且你想要跳过运行时检查的便利时,你才应该使用非空Assert。
Playground代码链接

gg0vcinb

gg0vcinb2#

那是完全不同的事情。
空Assert操作符!.是你,程序员,* 向编译器Assert * 你知道的事实,即 * 属性访问不会失败 *,编译器无法证明其原因。对于运行时错误,它并不比程序员向编译器做出的任何其他Assert更安全,你比编译器更清楚。

const foo = null;
foo!.someProperty; // compiles but you will get a TypeError! Cannot read property 'someProperty' of null or undefined.

另一个是可选的链接操作符,它基本上是以下常见Javascript模式的简写:

const something = foo && foo.bar && foo.bar.baz;

但这比简单的速记要好,因为如果其中一个值是falsey,而另一些falsey值支持属性访问,那么上面的方法将失败。

const something = foo?.bar?.baz;

就这样,就像Javascript版本一样,它是“安全的”,因为它保证不会因为试图访问空引用的属性而导致运行时错误。
在您遇到的“特定”情况下,您可能需要这样的内容:

const enteredText = textInputRef.current?.value || '';

很明显结果是一个字符串。

相关问题