Flavored types (optional symbol property intersection) lose discrimination when base type is a complex template literal

1 week ago 13
ARTICLE AD BOX

I'm using the flavored types pattern (optional discriminant property via symbol intersection) and noticed it silently stops working when the base type is a template literal with a union in one of its placeholders.

const FLAVOR = Symbol("flavor"); // ✅ Works: number base type VendorId = number & { [FLAVOR]?: "vendor" }; type MachineId = number & { [FLAVOR]?: "machine" }; declare const vendorId: VendorId; declare function acceptMachineId(id: MachineId): void; acceptMachineId(vendorId); // Error ✅ // ✅ Works: simple template literal base type SimplePhone = `+${number}`; type SimpleA = SimplePhone & { [FLAVOR]?: "a" }; type SimpleB = SimplePhone & { [FLAVOR]?: "b" }; declare const simpleB: SimpleB; declare function acceptSimpleA(a: SimpleA): void; acceptSimpleA(simpleB); // Error ✅ // ❌ Breaks: complex template literal with union in placeholder type CountryCode = 1 | 33 | 44; type PhoneNumber = `+${CountryCode}${number}${number}`; type Whistleblower = PhoneNumber & { [FLAVOR]?: "whistleblower" }; type Relay = PhoneNumber & { [FLAVOR]?: "relay" }; declare const relay: Relay; declare function acceptWhistleblower(w: Whistleblower): void; acceptWhistleblower(relay); // No error ❌ — should be rejected // ✅ Workaround: required (branded) property instead of optional const BRAND = Symbol("brand"); type BrandedWhistleblower = PhoneNumber & { [BRAND]: "whistleblower" }; type BrandedRelay = PhoneNumber & { [BRAND]: "relay" }; declare const brandedRelay: BrandedRelay; declare function acceptBrandedWhistleblower(w: BrandedWhistleblower): void; acceptBrandedWhistleblower(brandedRelay); // Error ✅

My question: Why does adding a union type in a template literal placeholder (e.g. +${CountryCode}... where CountryCode = 1 | 33 | 44) make two differently-flavored types mutually assignable? And is there any workaround other than switching to a required (branded) property?

Read Entire Article