代码之家  ›  专栏  ›  技术社区  ›  principiorum

我们应该使用!或者?。(可选链接)打字?

  •  0
  • principiorum  · 技术社区  · 4 年前

    我只是用React练习Typescript。当我看到这段代码时,我发现要么 ! ?. (可选链接)可以使用

    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 价值。还有 ?. 可选链接是为了防止在找不到属性并返回时出错 未定义 在上述情况下,我可以使用 ! ?. 甚至两者结合 !?. ,并且Typescript编译器没有抗议。那么,哪一种使用起来最好、最安全呢?

    2 回复  |  直到 4 年前
        1
  •  8
  •   jcalz    4 年前

    Optional chaining ?. 更安全的 使用比 non-null assertions ! .

    考虑以下界面:

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

    这个 bar 属性是可选的。如果它不存在,它就会存在 undefined 当你阅读它时。如果它确实存在,它将有一个 baz 类型属性 string 。如果您只是尝试访问 巴兹 不确定的财产 酒吧 如果定义了,你会得到一个 编译器错误 警告你可能 运行时错误 :

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

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

    function optChain(foo: Foo) {
        console.log(foo.bar?.baz.toUpperCase());
    }
    
    optChain({ bar: { baz: "hello" } }); // HELLO
    optChain({}); // undefined
    

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


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

    function nonNullAssert(foo: Foo) {
        console.log(foo.bar!.baz.toUpperCase());
    }
    
    nonNullAssert({ bar: { baz: "hello" } }); // HELLO
    nonNullAssert({}); // 💥 TypeError: foo.bar is undefined
    

    只有在满足以下条件时,才应使用非空断言 确保您的属性访问是安全的,并且您希望跳过运行时检查的便利性。


    Playground link to code

        2
  •  2
  •   Jared Smith    4 年前

    这些是完全不同的东西。

    空断言运算符 !. 是你,程序员, 向编译器断言 事实上,你知道 属性访问不能失败 由于编译器无法证明的原因。对于运行时错误,它并不比程序员向编译器做出的任何其他断言更安全,因为你更清楚它的作用。

    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;
    

    但这总比简写好,因为如果其中一个值是虚假的,但一些虚假的值支持属性访问,则上述操作将失败。使用null合并,您只需编写:

    const something = foo?.bar?.baz;
    

    你完了。就像Javascript版本一样,它是“安全的”,因为它保证在尝试访问空引用的属性时不会导致运行时错误。

    在the 特别 如果你在那里,你可能想要这样的东西:

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

    很明显,无论如何,结果都是一个字符串。

    推荐文章