Hone logo
Hone
Problems

TypeScript Nullish Coalescing Type Operator

This challenge focuses on replicating the behavior of TypeScript's nullish coalescing operator (??) at the type level. You will learn how to create types that elegantly handle potentially null or undefined values, providing a default value when necessary. This is a fundamental concept for writing robust and type-safe JavaScript/TypeScript applications.

Problem Description

Your task is to create a generic TypeScript type called NullishCoalesce<T, U> that mimics the runtime behavior of the nullish coalescing operator (??).

The nullish coalescing operator (??) returns its left-hand operand when it is not null or undefined. Otherwise, it returns its right-hand operand.

You need to define NullishCoalesce<T, U> such that:

  • If T is null or undefined, NullishCoalesce<T, U> should resolve to U.
  • Otherwise (if T is anything other than null or undefined), NullishCoalesce<T, U> should resolve to T.

This type should be able to handle various primitive types, object types, union types, and even null and undefined directly.

Examples

Example 1:

type Example1 = NullishCoalesce<string | null, number>;
// Expected type: string | number

Explanation: string | null is not solely null or undefined, so the type resolves to the left-hand operand, string | null. However, since U is number, the resulting union includes number as well. The type effectively becomes string | number.

Example 2:

type Example2 = NullishCoalesce<undefined, boolean>;
// Expected type: boolean

Explanation: The left-hand operand undefined is one of the nullish values. Therefore, the type resolves to the right-hand operand, boolean.

Example 3:

type Example3 = NullishCoalesce<number, string>;
// Expected type: number

Explanation: The left-hand operand number is not null or undefined. Therefore, the type resolves to the left-hand operand, number.

Example 4:

type Example4 = NullishCoalesce<null | { name: string }, { age: number }>;
// Expected type: { name: string } | { age: number }

Explanation: The left-hand operand is a union type null | { name: string }. Since null is present, and we want to default when T is nullish, we need to consider the non-nullish part of T and U. The type should effectively be { name: string } | { age: number }.

Example 5:

type Example5 = NullishCoalesce<string, string | undefined>;
// Expected type: string | undefined

Explanation: The left-hand operand string is not nullish. Therefore, the type resolves to string. However, since the right-hand operand U is string | undefined, the overall resulting type needs to be assignable to both. So, the type becomes string | undefined.

Constraints

  • The solution must be a single generic type alias named NullishCoalesce<T, U>.
  • The solution must use only standard TypeScript type manipulation features (e.g., conditional types, union types).
  • No runtime code is required; this is purely a type-level challenge.

Notes

Consider how conditional types in TypeScript can be used to check for null or undefined. You might need to use union types to combine the potential outcomes correctly. Think about how to elegantly handle the case where T itself is a union type that includes null or undefined. The goal is to return U only when T is specifically null or undefined, not when T is a union that might contain null or undefined but also has other non-nullish members.

Loading editor...
typescript