Null Handling β
In JavaScript and TypeScript, null
and undefined
are distinct values. null
is an intentional absence of a value, while undefined
represents a variable that has been declared but not assigned a value. JavaScript allows both null
and undefined
to be used interchangeably in some cases (e.g., loose equality comparisons), but TypeScript can enforce stricter handling with the strictNullChecks
option, preventing unintended null
or undefined
assignments unless explicitly allowed.
C# does not have undefined
; every variable must have a defined value. Value types (e.g., int
, bool
) cannot be null
unless explicitly made nullable using ?
(e.g., int? x = null;
). Reference types (e.g., string
, object
) can be null
by default. To improve null safety, C# includes nullable reference types (string? name = null;
), allowing developers to indicate which variables can be null
and leveraging compiler warnings to prevent unintended null
dereferences.
Nullability β
let x: string | null;
function findUser(name: string, email?: string ) {
if (!email?.trim()) {
// Handle case when email is null or zero length
}
}
let handle = email?.split("@")[0];
// Null forgiving operator
let handle = email!.split("@")[0];
string? x;
User[] FindUser(string name, string? email) {
if (string.IsNullOrWhiteSpace(email)) {
// Handle case when email is null or zero length
}
}
var handle = email?.Split("@")[0];
// Null forgiving operator
var handle = email!.Split("@")[0];
Null Coalescing β
let handle = email?.split("@")[0] ?? userId;
handle ??= "unknown"
// ππThese are equivalent
if (handle === null) {
handle === "unknown"
}
var handle = email?.Split("@")[0] ?? userId;
handle ??= "unknown"
// ππThese are equivalent
if (handle == null) {
handle == "unknown"
}
In C#, we can also use pattern matching with null
values like this:
class User {
public int Id { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
}
if (user is { FirstName: null, Id: > 10000 }) {
// FirstName is null and ID is greater than 10000
}
Tim Deschryver has the best writeup on pattern matching
Nullability with Generics β
type List<T> = { }
// "Elements are string or null or undefined"
let list: List<string | null | undefined>;
let arr: Array<string | null | undefined>[];
// List<T> is a standard library collection type
// "Elements are string or null"
List<string?> list;
string?[] arr;