最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Should we use ! (non-null assertion) or ?. (optional chaining) in TypeScript? - Stack Overflow

programmeradmin3浏览0评论

I'm learning TypeScript and React, and when I came across this code, I found that either ! (non-null assertion) and ?. (optional chaining) can be used.

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;

What I know about ! is that it tells Typescript that this value is never null nor undefined. On the other hand, ?. is used to prevent errors when no property found and instead returns undefined. In the above example, I can use either ! or ?. or even both of them combined !?., and Typescript compiler does not complain. So which one is best and safest to use?

I'm learning TypeScript and React, and when I came across this code, I found that either ! (non-null assertion) and ?. (optional chaining) can be used.

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;

What I know about ! is that it tells Typescript that this value is never null nor undefined. On the other hand, ?. is used to prevent errors when no property found and instead returns undefined. In the above example, I can use either ! or ?. or even both of them combined !?., and Typescript compiler does not complain. So which one is best and safest to use?

Share Improve this question edited Mar 9, 2023 at 21:59 nevermind 2,4382 gold badges23 silver badges38 bronze badges asked Jan 20, 2021 at 2:21 principiorumprincipiorum 6169 silver badges23 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 16

Optional chaining ?. is safer to use than non-null assertions !.

Consider the following interface:

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

The bar property is optional. If it doesn't exist it will be undefined when you read it. If it does exist it will have a baz property of type string. If you just try to access the baz property without making sure that bar is defined, you'll get a compiler error warning you about a possible runtime error:

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

Optional chaining has actual effects at runtime and short-circuits to an undefined value if the property you're trying to access does not exist. If you don't know for sure that a property exists, optional chaining can protect you from some runtime errors. The TypeScript compiler does not complain with the following code because it knows that what you are doing is now safe:

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

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

You should use optional chaining if you are not sure that your property accesses are safe and you want runtime checks to protect you.


On the other hand, non-null assertions have no effect whatsoever at runtime. It's a way for you to tell the compiler that even though it cannot verify that a property exists, you are asserting that it is safe to do it. This also has the effect of stopping the compiler from complaining, but you have now taken over the job of ensuring type safety. If, at runtime, the value you asserted as defined is actually undefined, then you have lied to the compiler and you might hit a runtime error:

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

nonNullAssert({ bar: { baz: "hello" } }); // HELLO
nonNullAssert({}); // 
发布评论

评论列表(0)

  1. 暂无评论