Skip to content

Instantly share code, notes, and snippets.

@KonradSzwarc
Created March 24, 2022 15:00
Show Gist options
  • Save KonradSzwarc/98ce649f40ec72067a237d7cf29e7d8d to your computer and use it in GitHub Desktop.
Save KonradSzwarc/98ce649f40ec72067a237d7cf29e7d8d to your computer and use it in GitHub Desktop.
Opaque types that can be nested in each other.
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