TypeScript covariant and inverse

preface

Connotation and denotation

Before we say covariant and contravariant, we introduce two concepts connotation and denotation

Connotation: denotation: all things with the unique properties reflected in the concept

Fruit is a juicy, sweet plant fruit that is rich in nutrients and AIDS in digestion. Fruit is a general term for the fruits and seeds of some edible plants. This is the connotation. Its extension includes everything that fits the definition: apples, pears, bananas

The smaller the connotation of the concept, the more scope covered, the more epitaxy.

Animal => Dog => Shiba => Juvenile Shiba

The relevant data

  • What are the meanings of “extension” and “connotation” in logic?
  • Baidu Encyclopedia – Connotation and denotation

LSP(Richter substitution Principle)

  • A subclass can implement an abstract method of the parent class, but cannot override a nonabstract method of the parent class
  • Subclasses can add their own special methods
  • When a subclass’s method overrides a parent class’s method, the method’s preconditions (that is, the method’s input parameters) are looser than the parent class’s method
  • When a subclass’s method implements a parent class’s method (overriding/overloading or implementing an abstract method), the method’s postcondition (that is, the output/return value of the method) is stricter or equal to that of the parent class’s method

define

Covariance and contravariance are terms used in computer science to describe whether there are parent/child relationships among multiple types of complex types constructed by type constructors

Covariant and contravariant – Wikipedia

I can look at the definition and I don’t have to look at the definition if I can understand it, but I can’t understand the definition.


👇 content is not necessarily correct, subjective processing, wrong spray

Covariant and inverter are features in the type system to better support LSPS

A simple description of covariant and contravariant is A summary of the relationship between the two types, similar to A=[1,2,3], B=[1,2]. Let’s say B is A subset of A, an abstract summary of the phenomenon,

Let’s describe this phenomenon

Subsequent strictFunctionTypes do not discuss ts bidirectional variants as opened

Playground

class Animal { base = ' ' }

class Dog extends Animal {
  type = 'Dog'
}

// The covariant and contravariant scenarios that occur are the return values of the assignment parameters

// covariant: type convergence narrowing intension and broadening intension
// contravariant: type scatter connotation expand epitaxial shrink

/ / assignment

let a: Animal  
let b: Dog

a = new Dog Dog => Animal; Dog => Animal
b = new Animal // error Property 'type' is missing in type' Animal' but required in type' Dog'.

/ / parameters

let fn1 = (animal: Animal) = > {}
let fn2 = (dog: Dog) = > {}

fn1 = fn2 // error Type '(dog: dog) => void' is not assignable to Type '(animal: animal) => void'.
fn2 = fn1 Dog => Dog => Dog => Dog => Dog => Dog

/ / the return value

let fx1:() = > Animal = () = > new Animal 
let fx2:() = > Dog = () = > new Dog

fx1 = fx2 Dog => Animal; Dog => Animal
fx2 = fx1 Animal' is not assignable to Type '() => Dog.

Copy the code

Covariant and contravariant scenarios will occur when the assignment parameter returns the value

From the above phenomenon, we can conclude the following law

Assignment is covariant

The return value of the function is covariant

The function argument is allowed to invert

understand

The first layer

nature

Covariant and contravariant conversions are safe to use

The second floor

Why does this happen

Type conversion in order to better meet the requirements of object-oriented programming, inheritance, polymorphism, LSP use, if strictly limited to each type conversion, then the development process will be more tedious

The third layer

How does this thing work

Implementationally, this is the case when B is originally tested and its inherited type is also compared to verify that it meets the safe conversion conditions.

TypeScript implements typescript-checker

Refer to the checkTypeRelatedTo method on line 17847 and mappedTypeRelatedTo on line 19419

skills

  1. The Union type is converted to the Union to Intersection type
type UnionToIntersection<U> = (U extends any ? (u: U) = > void : never) extends (a:infer A) => any ? A : never 

type cases = [
    Expect<Equal<UnionToIntersection<'foo' | 42 | true>, 'foo' & 42 & true>>,
    Expect<Equal<UnionToIntersection<(() = > 'foo') | ((i: 42) = > true>,() = > 'foo') & ((i: 42) = > true) > >,)Copy the code

Through extends’ foo ‘| | 42 true become a’ foo ‘= > void | 42 = > void | true = > void then using inverter function parameters into a’ foo ‘& 42 & true

Going from ‘foo’ or ’42’ or ‘true’ to being both ‘foo’ and ’42’ and ‘true’ and so his result is never and every function in that process expands its type, expands its content and shrinks its extension

  1. Tuple extraction function return value
class A{
  a = 1
}
class B extends A{
  b = 2
}

type FunToUnion<T extends any[]> = T[number] extends () => infer U ? U : never 

FunToUnion<[() = > string.() = > number] >// string | number
FunToUnion<[() = > A, () = > B]> // A

Copy the code

B => A has covariant type convergence and intension has shrunk and extension has expanded

String into a string | number we found clear extension, so content must be reduced, the types of convergence

reference

  • ts Type Compatibility
  • wikipedia
  • flow subtype
  • Covariant and contravariant
  • Covariance, contravariance and a little bit of TypeScript
  • How I understand Covariance & Contravariance in typescript
  • Covariance and Contravariance (C#)
  • Docs.microsoft.com/zh-cn/dotne…
  • difference-between-covariance-contra-variance