How to undestand relations between types any, unknown, {} and between them and other types?

后端 未结 2 1656
生来不讨喜
生来不讨喜 2020-11-30 12:57

Trying to understand relations between types I have this code

type CheckIfExtends = A extends B ? true : false;

type T1 = CheckIfExtends

        
2条回答
  •  攒了一身酷
    2020-11-30 13:53

    I will start from what means extends in TypeScript. On the first look on it, it behaves strange, as for product types (like objects) behaves like 'is superset' and for unions as 'is subset', also totally differently it works for function types. It can look strange at first, but it is logical behavior, in other words most types have soundness property.

    My rule of thumb to understand this concept is to read extends as assignable to. Then if x extends y, it means that x can be used whenever y is required.

    Lets consider three different algebraic data types, if for them above holds true.

    For product type

    type A = {a: string}
    type B = {a: string; b: number}
    type BextendsA = B extends A ? true : false // evaluates to true 
    

    Above is true because B can be used in every places where A is required, as B covers the whole structure A has. B is a superset of A. But what holds here is B is assignable to A.

    For union type

    type A = number | string
    type B = number
    type BextendsA = B extends A ? true : false // evaluates to true 
    

    Totally differently it looks for union. B for product was a superset, for union B is a subset! Yes, different logic, B does not represent all values possible in A, but whenever A is required B can be used instead. So B is assignable to A.

    For function types

    type A = (a: number) => void
    type B = () => void
    type BextendsA = B extends A ? true : false // evaluates to true 
    

    For function types, it looks even more weird as A looks like more specified function then B, so how B can extends A then? This goes from the assignability again, as whenever something needs A, we can assign B. This is very visible in example like Array.map. Consider:

    [1,2].map(x => x + 1)
    

    Array.map requires function which has three arguments - (el,index,arr) => any but can work with a function which has only one argument el => any. And again it holds the assignability, B is assignable to A.


    Types like any, unknown, {} are unsound, it means that their behavior cannot be proven logically. Understanding how they behave in TS is more just understanding the specification and reasons about such decisions. But it cannot be logically explained, as unsound type behave against logic.

    The places where TypeScript allows unsound behavior were carefully considered, and throughout this document we’ll explain where these happen and the motivating scenarios behind them.

提交回复
热议问题