Created
August 13, 2020 10:23
-
-
Save mikearnaldi/4f2c69a53a00c67edf842a4d4ed9cfbb to your computer and use it in GitHub Desktop.
This file contains 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
/** | |
* Base types | |
*/ | |
export type X = string; | |
export type Y = "nominal"; | |
export type Z = string | symbol; | |
export type Extends<A, B> = [A] extends [B] | |
? "sub-type" | |
: [B] extends [A] | |
? "super-type" | |
: "unrelated"; | |
export interface Covariant<T> { | |
covariant: () => T; | |
} | |
export interface Contravariant<T> { | |
contravariant: (_: T) => void; | |
} | |
/** | |
* Liskov coehrent type refinement | |
*/ | |
// coherent because Y extends X | |
export type YX = Extends<Y, X>; // sub-type | |
export interface CovariantExtended extends Covariant<X> { | |
covariant: () => Y; | |
} | |
// coherent because X extends Z | |
export type ZX = Extends<Z, X>; // super-type | |
export interface ContravariantExtended extends Contravariant<X> { | |
contravariant: (_: Z) => void; | |
} | |
/** | |
* Liskov non-coehrent type refinement | |
*/ | |
// @ts-expect-error | |
export interface ContravariantInvalidExtension extends Contravariant<X> { | |
contravariant: (_: Y) => void; | |
} | |
// @ts-expect-error | |
export interface CovariantInvalidExtension extends Covariant<X> { | |
covariant: () => Z; | |
} | |
/** | |
* Invariant = covariant + contravariant | |
* | |
* type { foo: string } =~ type { getFoo: () => string, setFoo: () => void } | |
* | |
* Where =~ represents an isomorphism of types | |
* | |
* getFoo represents the covariant side | |
* setFoo represents the contravariant side | |
*/ | |
export interface Foo { | |
foo: string; | |
} | |
// @ts-expect-error | |
export interface FooA extends Foo { | |
foo: Z; // breaks the covariant side | |
} | |
export interface FooB extends Foo { | |
foo: Y; // TS is unsound here because Y breaks the contravariant side (we are allowing mutability) | |
} | |
export declare const fooB: FooB; | |
// @ts-expect-error | |
fooB.foo = "correctly breaks"; // sure, I cannot assign a string to "foo" | |
// set foo in a generic Foo | |
export function setFoo(foo: Foo) { | |
foo.foo = "I am setting a string"; | |
} | |
// sure, FooB extends Foo..... | |
setFoo(fooB); | |
// @ts-expect-error | |
fooB.foo === "I am setting a string"; // runtime true, type level invalid |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment