Skip to content

Instantly share code, notes, and snippets.

@steinuil
Last active November 14, 2018 11:04
Show Gist options
  • Save steinuil/9ce22f408cc586b0abf3e66c9eb9197e to your computer and use it in GitHub Desktop.
Save steinuil/9ce22f408cc586b0abf3e66c9eb9197e to your computer and use it in GitHub Desktop.
type Nat = 0 | { succ: Nat };

type Succ<N extends Nat> = { succ: N };

type Prev<N extends Nat> =
  N extends Succ<infer M> ? M : N;

type Add<N extends Nat, M extends Nat> =
  N extends Succ<infer PrevN> ? { succ: Add<PrevN, M> } : M;

type CoinToNat<C extends Coin> =
  C extends Coin1 ? Succ<0> :
  C extends Coin2 ? Succ<Succ<0>> :
  C extends Coin5 ? Succ<Succ<Succ<Succ<Succ<0>>>>> :
  never;

type NatToCoin<N extends Nat> =
  N extends Succ<Succ<Succ<Succ<Succ<0>>>>> ? Coin5 :
  N extends Succ<Succ<0>> ? Coin2 :
  N extends Succ<0> ? Coin1 :
  never;

type AddCoinsNat<C1 extends Coin, C2 extends Coin> =
  Add<CoinToNat<C1>, CoinToNat<C2>>;

type AddCoins<C1 extends Coin, C2 extends Coin> =
  NatToCoin<AddCoinsNat<C1, C2>>;

const test1: AddCoins<1, AddCoins<2, 2>> = 5;
const test2: NatToCoin<Prev<CoinToNat<2>>> = 1;
export type Nat = 0 | { succ: Nat };
export type Succ<T> = { succ: T };

export type X<T> = keyof T extends string
  ? { [V in keyof T]: Exclude<keyof T, V> extends never ? 0 : X<Omit<T, V>> }
  : never;

const x: X<{ a: any; b: any }> = {
  a: { b: 0 },
  b: { a: 0 },
};

export type Y<T> = keyof T extends string
  ? Succ<
      { [V in keyof T]: Exclude<keyof T, V> extends never ? 0 : X<Omit<T, V>> }
    >
  : never;

export type A<T> = keyof T extends string ? T[keyof T] : never;

export type YA<T> = keyof T extends string
  ? keyof T extends 'succ'
    ? { 1: Succ<YA<T[keyof T]>>; 0: never }[keyof T extends 'succ' ? 1 : 0]
//      ^ [ts] '1' is referenced directly or indirectly in its own type annotation. [2502]
    : { 0: YA<T[keyof T]>; 1: never }[keyof T extends 'succ' ? 1 : 0]
  : T extends number
  ? 0
  : never;

export type CountProps<T> = YA<Y<T>>;

const a: A<{ a: number }> = 0;

const y: Y<{ a: any }> = {
  succ: { a: 0 },
};

const ya: YA<Y<{ a: any; b: any }>> = {
  succ: { succ: 0 },
};

let c: CountProps<{ a: any }>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment