Last active
June 27, 2021 15:59
-
-
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
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
/** | |
* # 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