Created
March 24, 2022 15:00
-
-
Save KonradSzwarc/98ce649f40ec72067a237d7cf29e7d8d to your computer and use it in GitHub Desktop.
Opaque types that can be nested in each other.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare const t: unique symbol; | |
declare const t1: unique symbol; | |
declare const t2: unique symbol; | |
interface Extend0<Token> { | |
readonly [t]: Token; | |
} | |
interface Extend1<Token> { | |
readonly [t1]: Token; | |
} | |
interface Extend2<Token> { | |
readonly [t2]: Token; | |
} | |
type Opaque<Type, Token = unknown> = Type & | |
(Type extends Extend0<infer T0> | |
? Type extends Extend1<infer T1> | |
? Extend2<Token> & Extend1<T1> & Extend0<T0> | |
: Extend1<Token> & Extend0<T0> | |
: Extend0<Token>); | |
// Example | |
type Currency = Opaque<number, 'Currency'>; | |
type Eur = Opaque<Currency, 'Eur'>; | |
type Usd = Opaque<Currency, 'Usd'>; | |
declare function eurToUsd(eur: Eur): Usd; | |
declare function addCurrencies<C extends Currency>(n1: C, n2: C): C; | |
const euroWallet = 10 as Eur; | |
const unknownWallet = 30 as Currency; | |
const usdWallet = eurToUsd(euroWallet); | |
// @ts-expect-error Currency type is to wide to be passed in place of Eur | |
eurToUsd(unknownWallet); | |
export const doubledUsdWallet = addCurrencies(usdWallet, usdWallet); | |
export const doubledUnknownWallet = addCurrencies(unknownWallet, unknownWallet); | |
// @ts-expect-error trying to add euros and dollars | |
addCurrencies(euroWallet, usdWallet); | |
// works but result type is widen to Currency | |
export const combinedWallet = addCurrencies(euroWallet, unknownWallet); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment