By Marty Pitt


2013-03-07 00:11:24 8 Comments

Does Typescript currently (or are there plans to) support the safe navigation operator of ?.

ie:

var thing = foo?.bar
// same as:
var thing = (foo) ? foo.bar : null;

Also, is there a more common name for this operator (it's incedibly hard to google for).

14 comments

@zoran404 2019-11-12 06:44:49

It's finally here!

Here are a few examples:

// properties
foo?.bar
foo?.bar()
foo?.bar.baz()
foo?.bar?.baz()

// indexing
foo?.[0]
foo?.['bar']

// check if a function is defined before invoking
foo?.()
foo.bar?.()
foo?.bar?.()

But it doesn't work exactly the same as your assumption.

Instead of evaluating

foo?.bar

to this little code snippet we are all used to writing

foo ? foo.bar : null

it actually evaluates to

(foo === null || foo === undefined) ?
    undefined :
    foo.bar

which works for all the falsey values like an empty string, 0 or false.

I just don't have an explanation as to why they don't compile it to foo == null

@Damian Green 2019-11-01 13:41:12

It's called optional chaining and It's in Typescript 3.7

Optional chaining lets us write code where we can immediately stop running some expressions if we run into a null or undefined

@basarat 2015-02-12 04:53:45

Upate

It just got released with TypeScrip 3.7 : https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/x

It is called optional chaining : https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/#optional-chaining

With it the following:

let x = foo?.bar.baz(); 

is equivalent to:

let x = (foo === null || foo === undefined) ?
    undefined :
    foo.bar.baz();

Old answer

There is an open feature request for this on github where you can voice your opinion / desire : https://github.com/Microsoft/TypeScript/issues/16

@Bernoulli IT 2019-11-06 10:08:34

Fine. But not an answer...

@superluminary 2019-10-02 15:45:24

The Elvis (?.) Optional Chaining Operator is supported in TypeScript 3.7.

You can use it to check for null values: cats?.miows returns null if cats is null or undefined.

You can also use it for optional method calling: cats.doMiow?.(5) will call doMiow if it exists.

Property access is also possible: cats?.['miows'].

Reference: https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-beta/

@rekire 2019-10-04 08:24:43

Please correct me, but the Elvis operator is at least in Kotlin ?:. Do you have a reference?

@superluminary 2019-10-04 13:21:38

@Mottie 2019-10-15 02:35:48

@György Balássy 2019-11-07 02:46:17

Announcement of the TypeScript 3.7 release mentions it: devblogs.microsoft.com/typescript/announcing-typescript-3-7

@Jamon Holmgren 2019-09-30 15:55:57

Not yet (as of September, 2019), but since the "safe navigation operator" is now at Stage 3, it's being implemented in TypeScript.

Watch this issue for updates:

https://github.com/microsoft/TypeScript/issues/16

Several engines have early implementations:

JSC: https://bugs.webkit.org/show_bug.cgi?id=200199

V8: https://bugs.chromium.org/p/v8/issues/detail?id=9553

SM: https://bugzilla.mozilla.org/show_bug.cgi?id=1566143

(via https://github.com/tc39/proposal-optional-chaining/issues/115#issue-475422578)

You can install a plugin to support it now:

npm install --save-dev ts-optchain

In your tsconfig.json:

// tsconfig.json
{
    "compilerOptions": {
        "plugins": [
            { "transform": "ts-optchain/transform" },
        ]
    },
}

I expect this answer to be out of date in the next 6 months or so, but hopefully it will help someone in the meantime.

@Angelos Pikoulas 2019-04-20 02:17:10

_.get(obj, 'address.street.name') works great for JavaScript where you have no types. But for TypeScript we need the real Elvis operator!

@Simon_Weaver 2018-11-12 07:33:10

I don't generally recommend this approach (watch out for performance concerns), but you can use the spread operator to shallow clone an object, which you can then access the property on.

 const person = { personId: 123, firstName: 'Simon' };
 const firstName = { ...person }.firstName;

This works because the type of 'firstName' is 'propagated' through.

I'll use this most frequently when I have a find(...) expression that can return null and I need a single property from it:

 // this would cause an error (this ID doesn't exist)
 const people = [person];
 const firstName2 = people.find(p => p.personId == 999).firstName;

 // this works - but copies every property over so raises performance concerns
 const firstName3 = { ...people.find(p => p.personId == 999) }.firstName;

There may be some edge cases with the way typescript infers types and this won't compile, but this should generally work.

@VeganHunter 2017-01-17 01:50:25

Operator ?. is not supported in TypeScript version 2.0.

So I use the following function:

export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
    if (typeof someObject === 'undefined' || someObject === null)
        return defaultValue;
    else
        return someObject;
}

the usage looks like this:

o(o(o(test).prop1).prop2

plus, you can set a default value:

o(o(o(o(test).prop1).prop2, "none")

It works really well with IntelliSense in Visual Studio.

@Rajab Shakirov 2017-03-25 16:50:24

This is exactly what I was looking for! It works in typescript 2.1.6.

@Simon_Weaver 2018-11-12 05:03:58

or you could call it elvis<T> ;-)

@VeganHunter 2018-11-13 06:18:40

Simon_Weaver, I call it "sad clown" :o(

@Benny Bottema 2018-04-19 19:56:23

As answered before, it's currently still being considered but it has been dead in the water for a few years by now.

Building on the existing answers, here's the most concise manual version I can think of:

jsfiddle

function val<T>(valueSupplier: () => T): T {
  try { return valueSupplier(); } catch (err) { return undefined; }
}

let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(val(() => obj1.a.b)); // 'c'

obj1 = { a: {} };
console.log(val(() => obj1.a.b)); // undefined
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

obj1 = {};
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

obj1 = null;
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

It simply silently fails on missing property errors. It falls back to the standard syntax for determining default value, which can be omitted completely as well.


Although this works for simple cases, if you need more complex stuff such as calling a function and then access a property on the result, then any other errors are swallowed as well. Bad design.

In the above case, an optimized version of the other answer posted here is the better option:

jsfiddle

function o<T>(obj?: T, def: T = {} as T): T {
    return obj || def;
}

let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(o(o(o(obj1).a)).b); // 'c'

obj1 = { a: {} };
console.log(o(o(o(obj1).a)).b); // undefined
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

obj1 = {};
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

obj1 = null;
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

A more complex example:

o(foo(), []).map((n) => n.id)

You can also go the other way and use something like Lodash' _.get(). It is concise, but the compiler won't be able to judge the validity of the properties used:

console.log(_.get(obj1, 'a.b.c'));

@Fenton 2013-03-07 06:27:46

This is defined in the ECMAScript Optional Chaining specification, so we should probably refer to optional chaining when we discuss this. Likely implementation:

const result = a?.b?.c;

The long and short of this one is that the TypeScript team are waiting for the ECMAScript specification to get tightened up, so their implementation can be non-breaking in the future. If they implemented something now, it would end up needing major changes if ECMAScript redefine their specification.

See Optional Chaining Specification

Where something is never going to be standard JavaScript, the TypeScript team can implement as they see fit, but for future ECMAScript additions, they want to preserve semantics even if they give early access, as they have for so many other features.

Short Cuts

So all of JavaScripts funky operators are available, including the type conversions such as...

var n: number = +myString; // convert to number
var b: bool = !!myString; // convert to bool

Manual Solution

But back to the question. I have an obtuse example of how you can do a similar thing in JavaScript (and therefore TypeScript) although I'm definitely not suggesting it is a graceful as the feature you are really after.

(foo||{}).bar;

So if foo is undefined the result is undefined and if foo is defined and has a property named bar that has a value, the result is that value.

I put an example on JSFiddle.

This looks quite sketchy for longer examples.

var postCode = ((person||{}).address||{}).postcode;

Chain Function

If you are desperate for a shorter version while the specification is still up in the air, I use this method in some cases. It evaluates the expression and returns a default if the chain can't be satisfied or ends up null/undefined (note the != is important here, we don't want to use !== as we want a bit of positive juggling here).

function chain<T>(exp: () => T, d: T) {
    try {
        let val = exp();
        if (val != null) {
            return val;
        }
    } catch { }
    return d;
}

let obj1: { a?: { b?: string }} = {
    a: {
        b: 'c'
    }
};

// 'c'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = {
    a: {}
};

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = {};

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = null;

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

@Kuncevič 2017-12-21 04:00:28

interesting but in my case (this.loop || {}).nativeElement saying Property 'nativeElement' does not exist on type '{}'. any this.loop typeof angular.io/api/core/ElementRef

@Fenton 2017-12-21 09:51:03

@Kuncevic - you need to either... 1) provide a compatible default in place of {}, or 2) use a type assertion to silence the compiler.

@Vitalii Vasylenko 2018-01-07 13:32:37

(foo||{}).bar; - Jesus...

@Simon_Weaver 2018-11-12 05:06:47

Assuming foo is an actual useful object : (foo || {}).bar generally isn't going to compile in typescript because {} won't be of the same type as foo. That's the problem that @VeganHunter's solution aims to avoid.

@Harps 2019-03-07 08:59:37

@Simon_Weaver then (foo || {bar}).bar will let the compiler run smoothly and I think that verbosity is acceptable.

@Simon_Weaver 2019-03-08 05:50:28

@harps actually this only compiles if bar is defined as a variable, which it wouldn't most likely be

@Cristian Traìna 2019-08-26 12:54:24

d should be defined as any. Why is it defined as T?

@phidias 2017-07-28 15:15:43

We created this util method while working on Phonetradr which can give you type-safe access to deep properties with Typescript:

/**
 * Type-safe access of deep property of an object
 *
 * @param obj                   Object to get deep property
 * @param unsafeDataOperation   Function that returns the deep property
 * @param valueIfFail           Value to return in case if there is no such property
 */
export function getInSafe<O,T>(obj: O, unsafeDataOperation: (x: O) => T, valueIfFail?: any) : T {
    try {
        return unsafeDataOperation(obj)
    } catch (error) {
        return valueIfFail;
    }
}

//Example usage:
getInSafe(sellTicket, x => x.phoneDetails.imeiNumber, '');

//Example from above
getInSafe(foo, x => x.bar.check, null);

@Drenai 2018-01-14 11:44:54

Cool!! Is there any caveats? I have a wrapper class with about 20 getters to write, every one of them has the following type of return - and all fields have to be null checked return this.entry.fields.featuredImage.fields.file.url;

@Ray Suelzer 2018-02-17 03:43:26

The only caveat might possibly be a performance impact, but I'm not qualified to speak to how the various JITers handle this.

@Donut 2013-03-07 00:21:21

I can't find any reference to it whatsoever in the TypeScript language specification.

As far as what to call this operator in CoffeeScript, it's called the existential operator (specifically, the "accessor variant" of the existential operator).

From CoffeeScript's documentation on Operators:

The accessor variant of the existential operator ?. can be used to soak up null references in a chain of properties. Use it instead of the dot accessor . in cases where the base value may be null or undefined.

So, the accessor variant of the existential operator appears to be the proper way to refer to this operator; and TypeScript does not currently appear to support it (although others have expressed a desire for this functionality).

@Marty Pitt 2013-03-07 00:35:33

"accessor variant of the existential operator". Naturally. So catchy, it's near impossible to forget. :). Thanks for the extremely thorough answer.

@Donut 2013-03-07 00:38:14

@MartyPitt Sure thing! I agree, I'd love to see a) wider adoption of an operator like this (C# please!) and b) a better name (the "safe navigation" operator from your linked blog post has a nice ring to it).

@Martin Vseticka 2015-07-28 20:04:22

github.com/Microsoft/TypeScript/issues/16 The issue was moved here.

@RationalDev likes GoFundMonica 2016-03-29 08:30:04

No go so far, from the issue, "The general tripwires for re-evaluating this would be a concrete ES proposal reaching the next stage, or a general consensus from the ES committee that this feature wouldn't happen for a long time"

@Mike Kellogg 2017-08-28 16:14:39

just to make it perfectly clear for anyone who is confused by this you would use it in this format: <p *ngIf="data.description?.length>150" > to ensure that no error is thrown if description doesn't exist on data object

@Enzoaeneas 2018-05-09 20:23:29

Angular implements this in it's templates: angular.io/guide/…

@k0enf0rNL 2018-09-26 08:08:35

In some other languages its called the "Elvis" operator

@c_froehlich 2019-10-20 09:05:30

It's announced for TypeScript 3.7.0 (github.com/microsoft/TypeScript/issues/…)

@Jose A 2016-11-01 13:27:37

Edit: I have updated the answer thanks to fracz comment.

TypeScript 2.0 released !. It's not the same as ?.(Safe Navigator in C#)

See this answer for more details:

https://stackoverflow.com/a/38875179/1057052

This will only tell the compiler that the value is not null or undefined. This will not check if the value is null or undefined.

TypeScript Non-null assertion operator

// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
    // Throw exception if e is null or invalid entity
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // Assert that e is non-null and access name
}

@fracz 2016-11-03 21:08:00

Not the same as ? because it asserts that the value is defined. ? is expected to silently fail / evaluate to false. Anyway, good to know.

@Jose A 2016-11-04 23:06:00

Now that I think about it... This answer is pretty pointless, because it does not do the "safe navigation" that the C# operator does.

@Llewey 2017-04-27 13:49:50

This answered my question, though. I knew about ?. from c# and tried it in typescript. It didn't work, but I saw that !. existed but didn't know what it did. I wondered if it was the same, did a google search, and found my way to this question which informed me that no, they are different.

@A. K-R 2013-03-08 12:34:45

Not as nice as a single ?, but it works:

var thing = foo && foo.bar || null;

You can use as many && as you like:

var thing = foo && foo.bar && foo.bar.check && foo.bar.check.x || null;

@A. K-R 2016-03-08 14:29:58

&& evaluates as long as the statement is true. If it is true, it returns the last value. If it is false, it returns the first value that evaluated to false. That may be 0, null, false etc. || returns the first value that evaluates to true.

@mt_serg 2017-12-14 08:29:32

Doesn't work well if the bar is defined but evaluates to false (like boolean false, or zero).

Related Questions

Sponsored Content

6 Answered Questions

[SOLVED] Typescript: Interfaces vs Types

  • 2016-05-15 01:53:52
  • user6101582
  • 86546 View
  • 507 Score
  • 6 Answer
  • Tags:   typescript

5 Answered Questions

[SOLVED] What is TypeScript and why would I use it in place of JavaScript?

  • 2012-10-02 16:37:58
  • Mohammed Thabet
  • 492021 View
  • 1627 Score
  • 5 Answer
  • Tags:   javascript typescript

15 Answered Questions

[SOLVED] TypeScript Converting a String to a number

  • 2013-02-02 23:34:18
  • Paul0515
  • 730921 View
  • 692 Score
  • 15 Answer
  • Tags:   typescript

8 Answered Questions

[SOLVED] get and set in TypeScript

  • 2012-10-10 19:52:14
  • MuriloKunze
  • 482107 View
  • 556 Score
  • 8 Answer
  • Tags:   typescript

7 Answered Questions

[SOLVED] Are strongly-typed functions as parameters possible in TypeScript?

  • 2013-02-01 02:56:27
  • vcsjones
  • 232123 View
  • 487 Score
  • 7 Answer
  • Tags:   typescript

2 Answered Questions

[SOLVED] How to rewrite this ES6 code in TypeScript?

  • 2016-12-02 10:01:49
  • Pranesh Ravi
  • 151 View
  • 0 Score
  • 2 Answer
  • Tags:   typescript

1 Answered Questions

Polymer typescript (ide) support

2 Answered Questions

[SOLVED] Why 'declare' is not working for 'extends' in typescript?

  • 2015-04-12 13:11:10
  • grigoryvp
  • 360 View
  • 0 Score
  • 2 Answer
  • Tags:   typescript

1 Answered Questions

TypeScript: Alternative to Multi-file class+module merging

  • 2014-06-30 17:58:54
  • masonk
  • 1107 View
  • 2 Score
  • 1 Answer
  • Tags:   typescript

Sponsored Content