At time of writing, TypeScript does not support the optional chaining operator. See discussion on the TypeScript issue tracker: https://github.com/Microsoft/TypeScript/issues/16
As a warning, the semantics of this operator are still very much in flux, which is why TypeScript hasn't added it yet. Code written today against the Babel plugin may change behavior in the future without warning, leading to difficult bugs. I generally recommend people to not start using syntax whose behavior hasn't been well-defined yet.
Answer from Ryan Cavanaugh on Stack OverflowVideos
At time of writing, TypeScript does not support the optional chaining operator. See discussion on the TypeScript issue tracker: https://github.com/Microsoft/TypeScript/issues/16
As a warning, the semantics of this operator are still very much in flux, which is why TypeScript hasn't added it yet. Code written today against the Babel plugin may change behavior in the future without warning, leading to difficult bugs. I generally recommend people to not start using syntax whose behavior hasn't been well-defined yet.
Update Oct 15, 2019
Support now exists in [email protected]
Say thanks to https://stackoverflow.com/a/58221278/6502003 for the update!
Although TypeScript and the community are in favor of this operator, until TC39 solidifies the current proposal (which at the time of this writing is at stage 1) we will have to use alternatives.
There is one alternative which gets close to optional chaining without sacrificing dev tooling: https://github.com/rimeto/ts-optchain
This article chronicles what the creators were able to achieve in trying to mirror the native chaining operator:
- Use a syntax that closely mirrors chained property access
- Offer a concise expression of a default value when traversal fails
- Enable IDE code-completion tools and compile-time path validation
In practice it looks like this:
import { oc } from 'ts-optchain';
// Each of the following pairs are equivalent in result.
oc(x).a();
x && x.a;
oc(x).b.d('Default');
x && x.b && x.b.d || 'Default';
oc(x).c[100].u.v(1234);
x && x.c && x.c[100] && x.c[100].u && x.c[100].u.v || 1234;
Keep in mind that alternatives like this one will likely be unnecessary once the proposal is adopted by TypeScript.
Also, a big thanks to Ryan Cavanaugh for all the work you are doing in advocating this operator to TC39!
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
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 the optional property accessor 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.
I'm trying to enforce the use of optional chaining for accessing properties of any type object in TypeScript to avoid potential runtime errors. For example, I want the following code to throw a TypeScript error:
catch(e: any) {
const code = e.code; // This should throw a TypeScript error
}But this code should not throw an error:
catch(e: any) {
const code = e?.code; // This should not throw a TypeScript error
}Is there a way to configure TypeScript to enforce this rule or any workaround to achieve this behavior?