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 }>;