type Eq<A, B> = <X>(a: A, eq: (x: A & B) => X) => X; const refute = (x: never) => x; const refl = <A, X>(a: A, eq: (x: A) => X) => eq(a); const sickos = <A>(x: A, eq: Eq<A, number>) => eq(x, (x) => x); const two = sickos(2, refl); type Ty<A> = | { tag: "number"; eq: Eq<A, number> } | { tag: "string"; eq: Eq<A, string> }; const number: Ty<number> = { tag: "number", eq: refl }; const string: Ty<string> = { tag: "string", eq: refl }; const show_number_or_string = <A>(x: A, ty: Ty<A>) => { if (ty.tag === "number") { return ty.eq(x, (x) => `number: ${x.toString()}`); } else if (ty.tag === "string") { return ty.eq(x, (x) => `string: ${x}`); } else { return refute(ty); } }; const printed_number = show_number_or_string(2, number); console.log(printed_number); const printed_string = show_number_or_string("a", string); console.log(printed_string);