Member-only story
Phantom Types
Phantom types are a type system feature used to add more safety and expressiveness to code without adding runtime overhead. it is used at the type level for compile-time checks.
What Happens Without Phantom Types?
If you don’t use phantom types, your code might lack compile-time guarantees about valid operations on certain entities, which can lead to runtime errors or logical bugs. For example, you might mistakenly perform an invalid operation on an object because the type system does not enforce the rules you intended.
Example Without Phantom Types:
Imagine a banking application where you have accounts that can either be active or closed.
struct Account {
let balance: Double
let isClosed: Bool
}
func withdraw(amount: Double, from account: Account) -> Account {
if account.isClosed {
fatalError("Cannot withdraw from a closed account")
}
guard account.balance >= amount else {
fatalError("Insufficient funds")
}
return Account(balance: account.balance - amount, isClosed: account.isClosed)
}
// Usage
let activeAccount = Account(balance: 1000, isClosed: false)
let closedAccount = Account(balance: 0, isClosed: true)
// Runtime crash if you forget to check isClosed
let updatedAccount = withdraw(amount: 100, from: closedAccount) // Fatal error: Cannot withdraw from a closed account
With phantom types, you move these checks to the type system, making it impossible to misuse the API at compile time.