Phantom types help us add extra information to types without changing the actual data. This keeps our code safer and clearer.
0
0
Phantom types in Typescript
Introduction
When you want to mark data with extra meaning without adding runtime cost.
To prevent mixing different kinds of similar data, like user IDs and product IDs.
When you want the compiler to catch mistakes about how data is used.
To add extra checks in your code without changing the data shape.
When you want to make your code easier to understand by showing intent in types.
Syntax
Typescript
type Phantom<Type, PhantomType> = Type & { __phantom?: PhantomType }Phantom types use a type parameter that does not affect the runtime value.
The extra property (like __phantom) is optional and never used at runtime.
Examples
This example creates a
UserId type that is really a string but marked specially to avoid mixing with other strings.Typescript
type UserId = string & { __brand: 'UserId' };
function getUser(id: UserId) {
console.log(`User ID is ${id}`);
}This shows how phantom types prevent mixing IDs that look the same but mean different things.
Typescript
type ProductId = string & { __brand: 'ProductId' };
const userId: UserId = 'abc123' as UserId;
const productId: ProductId = 'abc123' as ProductId;
// getUser(productId); // Error: Argument of type 'ProductId' is not assignable to parameter of type 'UserId'.Phantom types can help catch errors like adding meters and seconds, which don't make sense together.
Typescript
type Phantom<T, PhantomType> = T & { __phantom?: PhantomType };
type Meter = Phantom<number, 'Meter'>;
type Second = Phantom<number, 'Second'>;
const distance = 5 as Meter;
const time = 10 as Second;
// const wrong = distance + time; // Error: Operator '+' cannot be applied to types 'Meter' and 'Second'.Sample Program
This program defines phantom types for user and product IDs to avoid mixing them. It shows how the compiler helps catch mistakes.
Typescript
type Phantom<T, PhantomType> = T & { __phantom?: PhantomType };
type UserId = Phantom<string, 'UserId'>;
type ProductId = Phantom<string, 'ProductId'>;
function getUser(id: UserId) {
console.log(`User ID is ${id}`);
}
const userId = 'user-123' as UserId;
const productId = 'prod-456' as ProductId;
getUser(userId); // Works fine
// getUser(productId); // Uncommenting this line causes a compile-time errorOutputSuccess
Important Notes
Phantom types do not exist at runtime, so they add no extra cost.
Use as to tell TypeScript when you want to treat a value as a phantom type.
Phantom types help catch bugs early by making types more specific.
Summary
Phantom types add extra meaning to types without changing data.
They help prevent mixing similar but different data types.
They improve code safety and clarity with no runtime cost.