"For example, I could write someObject?.someProperty?.someMethod() which makes the error go away; but it ignores the fact that it is almost always an error if any of those null coalescing operators have effect."

If having a null value there is an exceptional situation, then your types should not allow that null. It sounds like your types are too permissive. If it's an error when the value is null, then it stands to reason it should be a type error when the value is null.

Dealing with nullish values is something that we all have to deal with, but it's really hard to generalize about since what to do in the null case is very dependent on how your app/code works.


In general, when I have properties that need be non-null most of the time, I declare them as such.

interface MyType {
  someObject: {
    someProperty: {
      someMethod(): void
    }
  }
}

And then only make the fields optional when required for that circumstance:

function makeMyType(values: Partial<MyType>): MyType {
  return { ...defaultMyType, ...values }
}

Sometimes it can't really be avoided easily, and in this cases I do end up doing a lot this:

if (!foo) throw new Error('really expected foo to be here!')
console.log(foo.bar)

Or in some cases just:

if (!foo) return
console.log(foo.bar)

Which is fine. If it ever really is nullish, then you get a nice error message about what went went wrong. Or, in the second case, you just bail from a function that would be invalid in the null case.


It's hard to advise more specifically without the details of your types. But in the end if:

"it seems like a non-negligble portion of my code is dealing with nullish-able variables"

then I would look at your types first to make sure you remove as much nullishness as you can.


Another option is to use a type predicate function to validate whole objects which you can then use without testing every single property everywhere:

interface Foo {
  foo: string
  bar: string
  baz: string
}

function validateFoo(maybeFoo: Partial<Foo>): maybeFoo is Foo {
  return !!maybeFoo.foo && !!maybeFoo.bar && !!maybeFoo.baz
}

const partialFoo: Partial<Foo> = { bar: 'a,b,c' }
if (validateFoo(partialFoo)) {
  partialFoo.bar.split(',') // works
}

Playground

Answer from Alex Wayne on Stack Overflow
Top answer
1 of 2
4

"For example, I could write someObject?.someProperty?.someMethod() which makes the error go away; but it ignores the fact that it is almost always an error if any of those null coalescing operators have effect."

If having a null value there is an exceptional situation, then your types should not allow that null. It sounds like your types are too permissive. If it's an error when the value is null, then it stands to reason it should be a type error when the value is null.

Dealing with nullish values is something that we all have to deal with, but it's really hard to generalize about since what to do in the null case is very dependent on how your app/code works.


In general, when I have properties that need be non-null most of the time, I declare them as such.

interface MyType {
  someObject: {
    someProperty: {
      someMethod(): void
    }
  }
}

And then only make the fields optional when required for that circumstance:

function makeMyType(values: Partial<MyType>): MyType {
  return { ...defaultMyType, ...values }
}

Sometimes it can't really be avoided easily, and in this cases I do end up doing a lot this:

if (!foo) throw new Error('really expected foo to be here!')
console.log(foo.bar)

Or in some cases just:

if (!foo) return
console.log(foo.bar)

Which is fine. If it ever really is nullish, then you get a nice error message about what went went wrong. Or, in the second case, you just bail from a function that would be invalid in the null case.


It's hard to advise more specifically without the details of your types. But in the end if:

"it seems like a non-negligble portion of my code is dealing with nullish-able variables"

then I would look at your types first to make sure you remove as much nullishness as you can.


Another option is to use a type predicate function to validate whole objects which you can then use without testing every single property everywhere:

interface Foo {
  foo: string
  bar: string
  baz: string
}

function validateFoo(maybeFoo: Partial<Foo>): maybeFoo is Foo {
  return !!maybeFoo.foo && !!maybeFoo.bar && !!maybeFoo.baz
}

const partialFoo: Partial<Foo> = { bar: 'a,b,c' }
if (validateFoo(partialFoo)) {
  partialFoo.bar.split(',') // works
}

Playground

2 of 2
4

I marked Alex Wayne's answer, above, as the accepted answer. It makes a lot of useful points about pain I've inflicted on myself.

But I'd like to take a moment to extol the virtues of nullCast, which has worked out far better than I expected.

function nullCast<TYPE>(value: TYPE | null | undefined) : TYPE
{
   if (!value) throw Error("Unexpected nullish value.");
   return value;
}

The principal beautify of nullCast is that one never needs to supply the template parameter. TypeScript will correctly infer the non-nullish return type automatically. This works.

  class Foo {
      optionalBarValue?: Bar;

      getBarValue() : Bar {
          // no explicit template parameter required.
          return nullCast(this.optionalBarValue); 
      }
  };

where "correct" means an exception will be thrown at runtime if this.optionalBarValue is undefined, and the automatically inferred return type of nullCast() at compile time is Bar, with any combination of | null, and | undefined stripped away.

Unlike the null-assertion operator '!' which produces no runtime code at all, nullCast does generate code, and does throw definitively. Compare

  let value: TYPE = this.objMember!.member!;
    // no effect at runtime'. An error is throw if .objMember 
    // evaluates to null or undefined, but no exception is thrown
    // if .member is nullish. The value variable ends up 
    // holding a value at runtime that is not of correct type.

   let value: TYPE = nullCast(this.objMember?.member);
     // Throws convincingly at runtime if either .objMember 
     // or .member evaluate to nullish. The inferred return type
     // of .member is non-nullish concrete type of .member.

There's nothing terribly wrong with using if statements to remove nullishness in following code. But it is intrusive, and has a negative effect on compactness and legibility. If your intention is to throw an error when nullish assumptions are violated, nullCast can be used compactly and efficiently inline when evaluating expressions, while still maintaining clear visiblity for readability purposes. e.g.:

let value1 = nullCast(this.member1);
let value2 = nullCast(this.member2.function()?.member);

or even (somewhat hesitantly)

let value = nullCast(this.function1)(arg1,arg2);

Motivation: One still needs to evaluate on a case-by-case basis what the correct response to a nullish value is. But the brutal easiness of if (!value) return; makes it far too easy to do the wrong thing. If a nullish value violates an assumption in code, the correct response is to throw at runtime, rather than return without regard for the delayed consequences of not doing what should have been done. nullCast, in certain cases, makes it easier to do the right thing.

Anyway. Back to doing my homework on type predicates.

🌐
TypeScript
typescriptlang.org › docs › handbook › release-notes › typescript-3-7.html
TypeScript: Documentation - TypeScript 3.7
The nullish coalescing operator is another upcoming ECMAScript feature that goes hand-in-hand with optional chaining, and which our team has been involved with championing in TC39.
🌐
JavaScript in Plain English
javascript.plainenglish.io › understanding-nullish-non-nullish-and-asserting-non-nullish-values-in-typescript-5a753fa0254d
Understanding Nullish, Non-Nullish, and Asserting Non-Nullish Values in TypeScript | JavaScript in Plain English
August 3, 2023 - In TypeScript, the term “nullish” refers to values that are null or undefined. These values represent the absence of a value. By default, variables are nullable (meaning they can hold these nullish values), unless you explicitly specify them as non-nullable using the type annotations or ...
🌐
W3Schools
w3schools.com › typescript › typescript_null.php
TypeScript Null & Undefined
When strictNullChecks is enabled, TypeScript requires values to be set unless undefined is explicitly added to the type.
🌐
TypeScript
typescriptlang.org › play › 3-7 › syntax-and-messaging › nullish-coalescing.ts.html
TypeScript: Playground Example - Nullish Coalescing
The nullish coalescing operator is an alternative to || which returns the right-side expression if the left-side is null or undefined. In contrast, || uses falsy checks, meaning an empty string or the number 0 would be considered false.
🌐
TypeScript
typescriptlang.org › docs › handbook › release-notes › typescript-2-0.html
TypeScript: Documentation - TypeScript 2.0
TypeScript has two special types, Null and Undefined, that have the values null and undefined respectively. Previously it was not possible to explicitly name these types, but null and undefined may now be used as type names regardless of type checking mode.
🌐
Typescript-training
typescript-training.com › course › fundamentals-v3 › 13-nullish-values
Nullish values | Learn TypeScript w/ Mike North
In this chapter we’ll dive deep into null, undefined, definite assignment, non-nullish coalescing, optional chaining and the non-null assertion operator. Although null, void and undefined are all used to describe “nothing” or “empty”, they are independent types in TypeScript.
🌐
Marius Schulz
mariusschulz.com › blog › nullish-coalescing-the-operator-in-typescript
Nullish Coalescing: The ?? Operator in TypeScript — Marius Schulz
August 14, 2021 - TypeScript 3.7 added support for the ?? operator, which is known as the nullish coalescing operator. We can use this operator to provide a fallback value for a value that might be null or undefined.
Find elsewhere
🌐
Medium
medium.com › @ambily_francis › unlocking-typescripts-nullish-coalescing-d00ef1db698a
Unlocking TypeScript’s Nullish Coalescing | by Ambily Francis | Medium
March 11, 2024 - In this example, the variable `username` is assigned the value `null`. When using nullish coalescing (`??`), if the value of `username` is `null` or `undefined`, the expression evaluates to the provided default value `’Guest’`. As a result, `defaultUsername` is assigned the value `’Guest’`.
🌐
LogRocket
blog.logrocket.com › home › optional chaining and nullish coalescing in typescript
Optional chaining and nullish coalescing in TypeScript - LogRocket Blog
June 4, 2024 - It was introduced in TypeScript 3.7 with the ?. operator. Optional chaining is often used together with nullish coalescing, which is the ability to fall back to a default value when the primary expression evaluates to null or undefined.
🌐
TypeScript ESlint
typescript-eslint.io › rules › prefer-nullish-coalescing
prefer-nullish-coalescing | typescript-eslint
*/ boolean?: boolean; /** Ignore number primitive types. */ number?: boolean; /** Ignore string primitive types. */ string?: boolean; } /** Ignore all primitive types. */ | true; /** Whether to ignore any ternary expressions that could be simplified by using the nullish coalescing operator.
🌐
Reddit
reddit.com › r/typescript › undefined vs null
r/typescript on Reddit: Undefined vs null
February 27, 2023 -

Since switching to TypeScript I have been using a lot of optional properties, for example:

type store = {
  currentUserId?: string
}

function logout () {
  store.currentUserId = undefined
}

However my coworkers and I have been discussing whether null is a more appropriate type instead of undefined, like this:

type store = {
  currentUserId: string | null
}

function logout () {
  store.currentUserId = null
}

It seems like the use of undefined in TypeScript differs slightly from in Javascript.

Do you guys/girls use undefined or null more often? And, which of the examples above do you think is better?

🌐
GeeksforGeeks
geeksforgeeks.org › nullish-coalescing-operator-in-typescript
Nullish Coalescing Operator (??) in TypeScript | GeeksforGeeks
July 9, 2024 - The nullish coalescing (??) operator is a logical operator in TypeScript that returns its right-hand side operand if the left-hand side operand is null or undefined; otherwise, it returns the left-hand side operand.
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Operators › Nullish_coalescing
Nullish coalescing operator (??) - JavaScript | MDN
The nullish coalescing operator treats undefined and null as specific values. So does the optional chaining operator (?.), which is useful to access a property of an object which may be null or undefined.
🌐
Reddit
reddit.com › r/typescript › why is `nonnullable` assignable to `object`?
r/typescript on Reddit: Why is `NonNullable<unknown>` assignable to `object`?
January 21, 2024 -

If I have a variable of type NonNullable<unknown> (equivalent to Object or {}), in other words, "any non-nullish value", why can I assign it to object (a.k.a "any object"), when they aren't the same thing?

Example:

const a: NonNullable<unknown> = 1; // Valid (makes sense)
const b: object = 1; // Invalid (makes sense since 1 is not an object)
const c: object = a; // Valid (doesn't make sense because `a` may not be an object as it's any non-nullish value)

This doesn't make any sense to me and seems to create runtime errors. For example, checking if an object has a given property.

Another issue I've seen which I'm not sure is related or not, is that type guards for nullish values on unknown values don't work as (I) expected, as in, it doesn't narrow the type to NonNullable<unknown>, even though NonNullable<unknown> | null | undefined should be equivalent to/the same as unknown.

Example:

const a: unknown = 1;
if (a === null || a === undefined) {
    console.log(a);
} else {
    console.log(a);
             // ^? - Still gives unknown
}

const a: undefined | null | NonNullable<unknown> = 1;
if (a === null || a === undefined) {
    console.log(a);
} else {
    console.log(a);
             // ^? - Correctly narrows to NonNullable<unknown>
}

Is it this problem related to the above issue? Or are they two different problems and if so what's going on with each?

🌐
egghead.io
egghead.io › lessons › typescript-use-the-nullish-coalescing-operator-in-typescript
Use the Nullish Coalescing Operator in TypeScript | egghead.io
This lesson introduces the ?? operator which is known as nullish coalescing. The ?? operator produces the value on the right-hand side if (and only if) ...
Published   February 17, 2021
🌐
xjavascript
xjavascript.com › blog › typescript-nullable-types
Understanding TypeScript Nullable Types — xjavascript.com
In TypeScript, by default, variables cannot hold null or undefined values unless explicitly allowed. Nullable types are created by using the union type operator (|) to combine a type with null or undefined.
🌐
overctrl
blog.overctrl.com › typescript-nullable-types-explained-your-comprehensive-guide-with-code-examples
TypeScript Nullable Types Explained: Your Comprehensive Guide with Code Examples
February 16, 2025 - This article provides an in-depth ... with practical examples. A nullable type refers to a type that can either hold a value of a specific type or be null or undefined....
🌐
Scaler
scaler.com › home › topics › typescript › optional chaining and nullish coalescing in typescript
Optional Chaining and Nullish Coalescing in TypeScript - Scaler Topics
May 4, 2023 - In the above example, we defined the class Person using the previously described type Undefined_String. Then we wrote a function that returned the person's complete name in uppercase. We must constantly verify the real value since the field we access may be null or undefined. Person::Uppercase_Name(), for example, returns undefined if the whole name is not specified. This method of implementation is time-consuming and difficult to read and maintain. As a result, TypeScript has included optional chaining and nullish coalescing in typescript version 3.7.