no-null-in-aliases
Avoid Including null or undefined in Type Aliases
Overview
Type aliases should represent something, not "something or nothing."
When you read User, you expect a user - not maybe-a-user. Include null explicitly at usage sites instead of hiding it in type aliases.
When to Use This Skill
- Defining type aliases
- Naming types that include null
- Understanding confusing nullability
- Designing function parameters
The Iron Rule
Type aliases should represent valid values.
Use Type | null explicitly, not NullableType.
Remember:
Usershould always be a userUser | nullis explicit about nullability- Hidden nullability confuses readers
- Properties in objects can be optional/nullable
Detection: Hidden Nullability
// Is user nullable? Can't tell from usage
function getComments(comments: Comment[], user: User) {
return comments.filter(c => c.userId === user?.id);
}
If the type is defined like this:
type User = { id: string; name: string; } | null;
Then the optional chain ?. is needed. But readers can't tell without checking the definition.
Better: Explicit Nullability
type User = { id: string; name: string; };
// Now nullability is visible at the usage site
function getComments(comments: Comment[], user: User | null) {
return comments.filter(c => c.userId === user?.id);
}
Or if user is required:
function getComments(comments: Comment[], user: User) {
return comments.filter(c => c.userId === user.id);
}
If You Must Include null
Use an explicit name:
// Bad: hidden null
type User = { id: string } | null;
// Better: explicit in name
type NullableUser = { id: string } | null;
// Best: no alias, explicit at usage
type User = { id: string };
function fn(user: User | null) { ... }
Nested Nullability is OK
This rule applies to the top level of type aliases. Nullable properties inside objects are fine:
// OK: nullable property in object
interface BirthdayMap {
[name: string]: Date | undefined;
}
// BAD: nullable at top level
type BirthdayMap = {
[name: string]: Date | undefined;
} | null;
Optional Properties
Optional properties are similar to nullable ones:
interface User {
id: string;
name: string;
email?: string; // OK: optional property
}
But consider Items 33 and 37 for guidance on when optional properties are appropriate.
Why This Matters
Code Readability
// What does this mean?
function processUser(user: User) { ... }
// If User includes null, you'd expect:
function processUser(user: User) {
if (!user) return; // But why? Isn't user required?
}
// Explicit is clearer:
function processUser(user: User | null) {
if (!user) return; // Ah, it might be null!
}
Refactoring Safety
// If User includes null, this might crash:
function processUser(user: User) {
console.log(user.name); // Runtime error if null
}
// With explicit typing, TypeScript catches it:
function processUser(user: User | null) {
console.log(user.name); // Error: 'user' is possibly 'null'
}
Pressure Resistance Protocol
1. "It's More Concise"
Pressure: "NullableUser is shorter than User | null"
Response: Explicitness at usage is worth a few characters.
Action: Use Type | null at usage sites.
2. "It's Always Nullable"
Pressure: "Users from the API are always nullable"
Response: That's an API detail, not a property of users themselves.
Action: Handle nullability at the API boundary, not in the type.
Red Flags - STOP and Reconsider
- Type alias ending with
| nullor| undefined - Types named
NullableXorMaybeX - Optional chains on parameters you thought were required
- Confusion about whether a type includes null
Common Rationalizations (All Invalid)
| Excuse | Reality |
|---|---|
| "It documents that null is possible" | It hides it; explicit | null documents it |
| "Less repetition" | Clarity > brevity |
| "It's how the data comes from API" | Transform at the boundary |
Quick Reference
// DON'T: null in type alias
type User = { id: string } | null;
// DON'T: Maybe/Nullable prefixes
type MaybeUser = User | null;
// DO: Clean type alias
type User = { id: string };
// DO: Explicit nullability at usage
function process(user: User | null) { ... }
// OK: Nullable properties inside objects
interface Config {
timeout?: number; // optional property
data: Data | null; // nullable property
}
The Bottom Line
Type names should represent the thing, not maybe-the-thing.
When you read User, you should expect a user. Include | null explicitly at usage sites where nullability matters. This makes code more readable and helps TypeScript catch null-related bugs.
Reference
Based on "Effective TypeScript" by Dan Vanderkam, Item 32: Avoid Including null or undefined in Type Aliases.