How to check for null value inline and throw an error in typescript?
Asked Answered
H

4

43

In C# I can write code to check for null references and in case throw a custom exception, example:

var myValue = (someObject?.SomeProperty ?? throw new Exception("...")).SomeProperty;

In recent updates TypeScript introduced the null coalescing operator ?? but using it like the above statement produces a compilation error. Is there some similar allowed syntax in TypeScript?


To clarify, the desired behavior is achieved with the following code:

  if(someObject?.someProperty == null) {
    throw new Error("...");
  }

  var myValue = someObject.someProperty.someProperty;

The code:

  var myValue = someObject?.someProperty.someProperty;

works logically ok but throws a less meaningful exception.

Heavyset answered 27/3, 2020 at 10:44 Comment(1)
Shouldn't that C# version be just var myValue = someObject?.SomeProperty ?? throw new Exception("..."); ? Or are you trying to get someObject.SomeProperty.SomeProperty?Durtschi
D
23

The reason for the syntax error is that throw is a statement, so you can't use it as the operand to an operator.

There is a JavaScript proposal for throw expressions working its way through the TC39 process, currently at Stage 2. If it gets to Stage 3 you can expect it will show up in TypeScript soon thereafter. (Update at the end of 2020: However, it seems to have stalled, having been blocked in Jan 2018 by a TC39 member who didn't think they "...were sufficiently motivated if we have do expressions..." Note that do expressions are still Stage 1 here at the end of 2020, but at least they were presented to TC39 in June.)

With a throw expression, you could write this (if you want the value of someObject.someProperty):

const myValue = someObject?.someProperty ?? throw new Error("custom error here");

Or if you want someObject.someProperty.someProperty (which is what I think your C# version does):

const myValue = (someObject?.someProperty ?? throw new Error("custom error here")).someProperty;

There's a Babel plugin for it you can use now. Here's the first example above on Babel's REPL.


Side note: You've said you want to throw a custom error, but for anyone else reading this who doesn't need a custom error:

If you want someObject.someProperty.someProperty, with no error if someObject is null/undefined but getting an error if someObject.someProperty is null/undefined, you can do:

const myValue = someObject?.someProperty.someProperty;

With that:

  • If someObject is null or undefined, myValue will get the value undefined
  • If someObject is not null or undefined but someObject.someProperty is null or undefined, you'll get an error because we didn't use ?. after the first someProperty.
  • If someObject and someObject.someProperty are both not null or undefined, myValue will get the result of looking up someObject.someProperty.someProperty.
Durtschi answered 27/3, 2020 at 10:53 Comment(2)
The proposal is not moving forward and it has been a long time... so I would remove the part about it moving forward soon(at the next meeting)Arroyo
@Arroyo - Yeah, last presented in 2018, blocked from consensus to move to Stage 3, and meanwhile the reason for that (do expressions) is still at Stage 1. But at least do expressions were presented at the June 2020 meeting.Durtschi
C
51

As long as TypeScript doesn't support this natively, you could write a function similar to this one:

function throwExpression(errorMessage: string): never {
  throw new Error(errorMessage);
}

which would then allow you to throw an error as expression:

const myString = nullableVariable ?? throwExpression("nullableVariable is null or undefined")
Clavicytherium answered 11/1, 2021 at 11:54 Comment(3)
A throw helper function is an interesting approach, thanks for sharing!Heavyset
Very nice! Thanks for this answer.Osteogenesis
Do note that this will goof up your stacktrace a bit.Granulose
D
23

The reason for the syntax error is that throw is a statement, so you can't use it as the operand to an operator.

There is a JavaScript proposal for throw expressions working its way through the TC39 process, currently at Stage 2. If it gets to Stage 3 you can expect it will show up in TypeScript soon thereafter. (Update at the end of 2020: However, it seems to have stalled, having been blocked in Jan 2018 by a TC39 member who didn't think they "...were sufficiently motivated if we have do expressions..." Note that do expressions are still Stage 1 here at the end of 2020, but at least they were presented to TC39 in June.)

With a throw expression, you could write this (if you want the value of someObject.someProperty):

const myValue = someObject?.someProperty ?? throw new Error("custom error here");

Or if you want someObject.someProperty.someProperty (which is what I think your C# version does):

const myValue = (someObject?.someProperty ?? throw new Error("custom error here")).someProperty;

There's a Babel plugin for it you can use now. Here's the first example above on Babel's REPL.


Side note: You've said you want to throw a custom error, but for anyone else reading this who doesn't need a custom error:

If you want someObject.someProperty.someProperty, with no error if someObject is null/undefined but getting an error if someObject.someProperty is null/undefined, you can do:

const myValue = someObject?.someProperty.someProperty;

With that:

  • If someObject is null or undefined, myValue will get the value undefined
  • If someObject is not null or undefined but someObject.someProperty is null or undefined, you'll get an error because we didn't use ?. after the first someProperty.
  • If someObject and someObject.someProperty are both not null or undefined, myValue will get the result of looking up someObject.someProperty.someProperty.
Durtschi answered 27/3, 2020 at 10:53 Comment(2)
The proposal is not moving forward and it has been a long time... so I would remove the part about it moving forward soon(at the next meeting)Arroyo
@Arroyo - Yeah, last presented in 2018, blocked from consensus to move to Stage 3, and meanwhile the reason for that (do expressions) is still at Stage 1. But at least do expressions were presented at the June 2020 meeting.Durtschi
G
22

If you're interested in throwing the error within a single line, you could wrap it in an immediately invoked function expression:

const test = null ?? (() => {throw new Error("Test is nullish")})();
Garnes answered 1/9, 2021 at 11:47 Comment(1)
Do you know how to setup prettier so it won't reformat this into multiple lines?Espino
A
1

I'm a little late to the party, but I bet this is what you want. I just tossed it in my utils.ts file:

export function ensuredDefined<T>(item: T | undefined, message?: string): T {
  if (item === undefined) {
    throw new Error(message ?? 'item is undefined');
  }
  return item;
}

Full disclosure I haven't tested this yet, but, I think this is what you want.

So instead of

const color = unusedColors.shift();
if (color === undefined) {
  throw new Error(...);
}
player.color = color;

I have

player.color = ensuredDefined(unusedColors.shift());

I bet that means you can write

var myValue = ensureDefined(someObject?.someProperty).someProperty
Anthologize answered 21/10, 2023 at 18:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.