Skip to content

Instantly share code, notes, and snippets.

@gvergnaud
Last active June 27, 2021 15:59
Show Gist options
  • Save gvergnaud/813874de83276bb39e28d464c20010b1 to your computer and use it in GitHub Desktop.
Save gvergnaud/813874de83276bb39e28d464c20010b1 to your computer and use it in GitHub Desktop.
How does the extends keyword work in typescript. Interactive playground: https://bit.ly/2XdCEfn
/**
* # How does `A extends B` work in TypeScript?
*
* If you think about types in terms of sets containing possible values,
* the `string` type is the set of all possible strings,
* the `number` type is the set of all possible numbers,
* the `'hello'` type is a set containing only the string 'hello'
* and the `2` type is a set containing only the number 2.
*
* Then you can think of `A extends B` as asking this question:
* "Is each value contained in A also contained in B?"
* or "Is A a subset of B?"
* or "Is B a superset of A?"
*
* The comparison isn't strict (it isn't a "proper subset", to use set theory jargon),
* so A can also be the same type as B. Think of it as the `<=` operator for numbers.
* `A extends B` is kinda equivalent to `A <= B`.
*
* Let's see a few examples:
**/
// Read this type as "B contains A".
type Extends<A,B> = A extends B ? true : false
// literals and primitive types
type T0 = Extends<string, string> // true, extends is always true when comparing a type to itself. It works like `<=` on numbers.
type T1 = Extends<'hello', string> // true, `string` contains all string literals
type T2 = Extends<string, 'hello'> // false, all strings aren't contained in 'hello'
type T3 = Extends<2, number> // true, `number` contains all number literals
type T4 = Extends<2, string> // false, `2` is not in `string`
// any
type T5 = Extends<'hello' | string| 2 | number, any> // true, everything is contained in any
type T6 = Extends<any, string> // true or false!!, some types in any are strings, other are not
/**
* If T6 can be `true | false`, that's because conditional types are distributive
* meaning each type in the `any` union will be tested to see if it extends string,
* and we will get back the union of all results.
*
* More info on conditional types distributivity here:
* https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types
**/
// never
type T7 = Extends<2, never> // false, never is an empty set, nothing is contained in it
type T8 = never extends number ? true : false // true, since never is the empty set, every other set contains it
type T9 = never extends never ? true : false // true, for the same reason
type T10 = never extends any ? true : false // true
type T11 = Extends<never, any> // never. For some reason this is not equivalent to the line above. Don't know if it is a bug or not.
// unknown
type T12 = Extends<'hello' | string| 2 | number, unknown> // true, everything is contained in unknown (just like any)
type T13 = Extends<unknown, string> // false, unlike `any`, unknown isn't part of any other type.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment