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.
@yayajacky
Copy link

Very nice

@frangio
Copy link

frangio commented Jan 14, 2020

foo() === undefined is a type error if foo() returns void

This doesn't seem to be true. Playground Link

@anka-213
Copy link

Is it possible to assign an unknown value to a {} | null | undefined value since they are equivalent? How do you consume unknown values?

@nin-jin
Copy link

nin-jin commented Apr 5, 2020

image

@fhpriamo
Copy link

Dude, that's amazing. Thank you.

@SerkanSipahi
Copy link

Thanks 👍

@laughinghan
Copy link
Author

@anka-213 You're right—and more generally, an equality check between a subtype and supertype are always allowed, duh

@laughinghan
Copy link
Author

Another mistake: there's actually no Infinity or NaN number literal type, because NaN is an ill-behaved value, so you can't type-narrow with ===. (There seems to be no good reason for the lack of an Infinity literal type.) microsoft/TypeScript#28682

Also, omissions, which I haven't fixed:

@tjjfvi
Copy link

tjjfvi commented Feb 5, 2021

Nothing besides itself (never) and any are assignable to never, but never is assignable to anything at all.

any is not assignable to never

@laughinghan
Copy link
Author

@tjjfvi Oh damn, you're right! Fixed the text, haven't fixed the diagram because I don't have the original editable version, I don't even remember how I made it lol

Also how are you all even finding this?

@tjjfvi
Copy link

tjjfvi commented Feb 11, 2021

@laughinghan I was linked to it on the typescript discord, but this gist is the top google result for "typescript type diagram".

@whoisYeshua
Copy link

@tjjfvi Oh damn, you're right! Fixed the text, haven't fixed the diagram because I don't have the original editable version, I don't even remember how I made it lol

Also how are you all even finding this?

So, I think we need to fix this part as well:

Anything except never is assignable to "any", and "any" is assignable to anything at all.

to:

Anything is assignable to "any", and "any" is assignable to anything at all (except never).

declare const any: any;
const never: never = any; // Error
declare const never2: never
const any2: any = never // Ok

@whoisYeshua
Copy link

TS_types_fixed-removebg

@workjacinjiyan
Copy link

TS_types_fixed-removebg

I am guessing this is the most accurate one

@nin-jin
Copy link

nin-jin commented Apr 22, 2022

type A = never extends any ? 1 : 2 // 1
type B = any extends never ? 1 : 2 // 1 | 2

Any is a jocker.

@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