Add nominal typing to string identifiers
TypeScript
// Branded types: separate ID kinds that are still plain strings at runtime.
declare const brand: unique symbol;
type Brand<T, B extends string> = T & { readonly [brand]: B };
type UserId = Brand<string, 'UserId'>;
type OrderId = Brand<string, 'OrderId'>;
function asUserId(value: string): UserId {
if (!/^u_[a-z0-9]{6,}$/.test(value)) throw new TypeError(`bad user id: ${value}`);
return value as UserId;
}
function asOrderId(value: string): OrderId {
if (!/^o_[a-z0-9]{6,}$/.test(value)) throw new TypeError(`bad order id: ${value}`);
return value as OrderId;
}
function loadOrder(orderId: OrderId): string {
return `loaded ${orderId}`;
}
const orderId = asOrderId('o_abc123');
console.log(loadOrder(orderId));
// loadOrder(asUserId('u_abc123')); // type error: UserId is not OrderId