Skip to content

Instantly share code, notes, and snippets.

@laughinghan
Last active June 19, 2024 10:18
Show Gist options
  • Save laughinghan/31e02b3f3b79a4b1d58138beff1a2a89 to your computer and use it in GitHub Desktop.
Save laughinghan/31e02b3f3b79a4b1d58138beff1a2a89 to your computer and use it in GitHub Desktop.
Diagram of every possible TypeScript type

Hasse diagram of every possible TypeScript type

  • any: magic, ill-behaved type that acts like a combination of never (the proper bottom type) and unknown (the proper top type)
    • Anything except never is assignable to any, and any is assignable to anything at all.
    • Identities: any & AnyTypeExpression = any, any | AnyTypeExpression = any
    • Key TypeScript feature that allows for gradual typing.
  • unknown: proper, well-behaved top type
    • Anything at all is assignable to unknown. unknown is only assignable to itself (unknown) and any.
    • Identities: unknown & AnyTypeExpression = AnyTypeExpression, unknown | AnyTypeExpression = unknown
    • Prefer over any whenever possible. Anywhere in well-typed code you're tempted to use any, you probably want unknown.
    • Equivalent to {} | null | undefined.
  • never: proper, well-behaved bottom type
    • Nothing besides itself (never) is assignable to never, but never is assignable to anything at all.
    • Identities: never & AnyTypeExpression = never, never | AnyTypeExpression = AnyTypeExpression
    • You'll see it in error messages related to exceptions and exhaustiveness checking, but you'll rarely write it outside of conditional types, where it's useful for "negating" a condition.
  • null: the only proper unit type, though there are two other unit-like types
    • Nothing besides itself (null), never, and any are assignable to null. null is assignable to itself (null)
  • undefined: unit-like type. Not quite a proper unit type because it's assignable to void (unit types are normally only assignable to themselves and the top type)
    • Nothing besides itself (undefined), never, and any are assignable to undefined. undefined is only assignable to void and, as always, itself (undefined), unknown, and any.
  • void: irregular [unit-like type]. Not a proper unit type because undefined is assignable to it (normally nothing is assignable to a unit type except itself and the bottom type)
    • Besides itself (void), never, and any, only undefined is assignable to void.void is only assignable to itself (void), unknown, and any.
    • Irregular because the set of values in this type is equal to the set of values that the undefined type consists of, specifically, the JavaScript value undefined. Normally that would make them the same unit type rather than two distinct unit-like types.
    • Useful to distinguish functions whose return value you aren't supposed to use at all, from functions whose return value may be the value undefined. E.g. if you have foo: () => void and bar: (x?: string) => number, then bar(foo()) is a type error.
  • boolean, number, string, symbol: primitive types, all disjoint
    • All the primitive types are all disjoint from each other and from null, undefined, and object, which means none of them are assignable to each other. Only themselves and their literal types (and never and any) are assignable to them, that is:
      • Only true and false are assignable to boolean (in fact, boolean is equivalent to true | false).
      • Only number literal types like 0, 1, 42, -7, 3.14 are assignable to number.
      • Only string literal types like "", "asdf", "etc." are assignable to string.
      • There are no symbol literals, the only way to create symbols is the Symbol constructor.
    • Primitive types are assignable to any subtype of the corresponding interface, for example number, and any number literal type, is assignable to the interface Number, or any subtype such as the interface { toFixed(): string }.
      • This implies all 4 of these primitive types are assignable to {}.
  • interface types: { whatever: AnyTypeExpression }
    • Assignable to subtypes like { a: number, b: string } is assignable to { a: number }, and { a: number } is assignable to { readonly a: number } or { a?: number } or { [prop: string]: number }.
    • There are many built-in interface types, like Object, Number, Date, RegExp, Array, ReadonlyArray, etc.
      • Object, {}, { toString(): string }, and all other subtypes of the Object interface are all the same type, thanks to the Object interface being a pseudo-top type.
      • Array<T> and ReadonlyArray<T> can also be written with the syntactic sugar T[] and readonly T[], respectively.
  • object: magic, but well-behaved refinement type, essentially {} but excluding the primitive types
    • Would be equivalent to Exclude<{}, boolean | number | string | symbol>, if Exclude<_,_> worked on interface types that aren't union types.
@mathcodes
Copy link

image

Great Diagram. 🙏

@nin-jin
Copy link

nin-jin commented Mar 31, 2023

@wuxin0011
Copy link

nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment