Implementing a Non-Nullable Helper Function in TypeScript
TypeScript's type system is powerful, but sometimes you need a concise way to ensure a value is not null or undefined. This challenge asks you to implement a helper function that takes a value and returns a non-nullable version of it, effectively asserting that the value is not null or undefined. This is useful for simplifying code and improving type safety when dealing with potentially nullable values.
Problem Description
You need to create a TypeScript function called nonNullable that accepts a value of any type T and returns a value of type NonNullable<T>. The NonNullable<T> utility type in TypeScript removes null and undefined from the union of types that T can be. Your function should effectively replicate this behavior.
Key Requirements:
- The function must be type-safe. It should correctly infer the non-nullable type based on the input.
- The function should return the original value if it is already non-nullable.
- The function should throw a
TypeErrorif the input isnullorundefined. This is to explicitly signal that the input was not a valid value for the intended operation.
Expected Behavior:
- If the input is
nullorundefined, the function should throw aTypeErrorwith a descriptive message. - If the input is a non-nullable value (e.g., a string, number, object, or a type that explicitly excludes
nullandundefined), the function should return the input value unchanged. - The function should preserve the type information of the non-nullable value.
Edge Cases to Consider:
- Input of
null - Input of
undefined - Input of a primitive type (string, number, boolean)
- Input of an object
- Input of a union type that includes
nullorundefined(e.g.,string | null)
Examples
Example 1:
Input: nonNullable(null)
Output: TypeError: Input cannot be null or undefined.
Explanation: The input is null, so a TypeError is thrown.
Example 2:
Input: nonNullable(undefined)
Output: TypeError: Input cannot be null or undefined.
Explanation: The input is undefined, so a TypeError is thrown.
Example 3:
Input: nonNullable("hello")
Output: "hello"
Explanation: The input is a non-nullable string, so it's returned unchanged.
Example 4:
Input: nonNullable(123)
Output: 123
Explanation: The input is a non-nullable number, so it's returned unchanged.
Example 5:
Input: nonNullable({ name: "John" })
Output: { name: "John" }
Explanation: The input is a non-nullable object, so it's returned unchanged.
Example 6:
Input: nonNullable<string | null>("world")
Output: "world"
Explanation: The input is a string, which is non-nullable, so it's returned unchanged.
Example 7:
Input: nonNullable<string | null>(null)
Output: TypeError: Input cannot be null or undefined.
Explanation: The input is null, so a TypeError is thrown.
Constraints
- The function must be written in TypeScript.
- The function must throw a
TypeErrorwhen the input isnullorundefined. - The function should handle any valid TypeScript type as input.
- The function should not introduce any unnecessary dependencies.
- The function should be relatively performant (avoiding complex or inefficient operations).
Notes
Consider using TypeScript's conditional types to achieve the desired type narrowing. Think about how to handle the case where the input is already non-nullable without throwing an error. The goal is to create a robust and type-safe helper function that can be used throughout your TypeScript projects.