score:4

Accepted answer

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({}); // 💥 typeerror: foo.bar is undefined

you should only use non-null assertions if you are sure that your property accesses are safe, and you want the convenience of skipping runtime checks.


playground link to code

score:2

those are completely different things.

the null assertion operator !. is you, the programmer, asserting to the compiler that you know for a fact that the property access cannot fail for reasons the compiler cannot prove. it is no more safe from a runtime error than any other assertion that you the programmer make to the compiler that you know better that it does.

const foo = null;
foo!.someproperty; // compiles but you will get a typeerror! cannot read property 'someproperty' of null or undefined.

the other is the optional chaining operator. it is basically shorthand for this common javascript pattern:

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

but it's better than just shorthand, because the above will fail if one of those values is falsey but some falsey values support property access. with null coalescing you just write:

const something = foo?.bar?.baz;

and you're done. and just like the javascript version it's "safe" in that it's guaranteed not to result in a runtime error from trying to access a property of a null reference.

in the particular case you have there, you probably want something like this:

const enteredtext = textinputref.current?.value || '';

where it's very clear that the result is a string no matter what.


Related Query

More Query from same tag