Have you ever learned/used TS:

  1. Read all kinds of TS documents, write up or vaguely understand;
  2. Contact with some TS keywords, not very skilled in use, lack of clear theoretical reference;
  3. TS has been downgraded to JS;
  4. Type in do not know where to break the layer, no longer connected;

If you think I’m taking a peek at your work 🐶, then this article might be perfect for you. Condtion Type (condtion type)

Think about a couple of demos

Are you confused by the following TS types?

  1. Puzzle problem 1
type Extract<T,U> = T extends U ? T : never ; Extract<"a" | "b" | "c", "a"> // "a" // Why ? !!!!!!!!!Copy the code

Why type Extract can Extract the intersection of T and U?

  1. Puzzle problem 2
type Length1<T> = T['length'] 
// error Type '"length"' cannot be used to index type 'T'.(2536)

type Length2<T> = T extends any[] ? T['length'] : never; 
// success 
Copy the code

Why type Length1 fails while Length2 can correctly obtain the length of T

  1. Puzzle problem 3
type Case<T> = [T] extends [(args: infer R) => any] ? R : never;
type Demo = Case<((args: { name: string; }) => any) | ((args: { age: number; }) => any)>  
// { name: string } & { age: number }
Copy the code

{name: string} & {age: number}

condition type

Refer to the link: www.typescriptlang.org/docs/handbo…

In fact, the above demo uses the condition type knowledge. Now you can understand why Typescript is so amazing 😄

define

Typescript2.8 adds conditional types that can represent inconsistent types. A conditional type inferences one of two possible types based on the Type Relationship test. Typescript2.8 adds conditional types that can represent inconsistent types. A conditional type inferences one of two possible types based on the Type Relationship test.

T extends U ? X : Y 
Copy the code

The above type indicates that if T can be assigned to U, this type is X, otherwise Y.

Distributive Conditional Types

If there is a checked type in the conditional type, it is called naked Type Parameter, also known as distributive Conditional types. If this type is replaced by a union type, the condition type is automatically assigned. For example, if T extends U? X: Y instance type type T is assigned to A | B | C. This type is assigned to (A extends U? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y).

Examples are as follows:

type TypeName<T> = T extends string
  ? "string"
  : T extends number
  ? "number"
  : T extends boolean
  ? "boolean"
  : T extends undefined
  ? "undefined"
  : T extends Function
  ? "function"
  : "object";

type T1 = TypeName<string | (() => void)>; // "string" | "function"
Copy the code

It is important to note that the prerequirement for distribution condition types is the existence of a stereotype. Without a stereotype, ordinary extends expressions do not distribute condition types. See the following example

type a = 'a' | 'b' | 'c' extends 'a' ? 'a' : never; // never, there is no type distribution, so the output is neverCopy the code

In fact, this explains Demo1.

type Extract<T,U> = T extends U ? T : never ;
Extract<"a" | "b" | "c", "a"> 
=  "a" extends "a" ? "a" : never | "b" extends "a" ? "a" : never | "c" extends "a" ? "a" : never
=  "a" | never | never 
=  "a"
Copy the code

Implicit type assignment

In instantiations of a distributive conditional type T extends U ? X : Y, references to T within the conditional type are resolved to individual constituents of the union type (i.e. T refers to the individual constituents after the conditional type is distributed over the union type). Furthermore, references to T within X have an additional type parameter constraint U (i.e. T is considered assignable to U within X).

In the True branch of conditional types, the type T is considered a subtype of type U. This sentence is difficult to understand, but let’s use the following example. We want to get the length property of type T.

type Length1<T> = T['length'] 
// error Type '"length"' cannot be used to index type 'T'.(2536)

type Length2<T> = T extends { length: number } ? T['length'] : 0; 
// success 
Copy the code

Error in type Length1 because the type of T is unknown, type T may not have length index. So what do we do if we want the compiler to know that type T has length? This is where you can use the above knowledge. T extends {length: number}; T extends {length: number}; T extends {length: number};

Covariant and contravariant condition types

For each type variable introduced by an infer (more later) declaration within U collect a set of candidate types by inferring from T to U (using the same inference algorithm as type inference for generic functions). For a given infer type variable V, if any candidates were inferred from co-variant positions, the type inferred for V is a union of those candidates. Otherwise, if any candidates were inferred from contra-variant positions, the type inferred for V is an intersection of those candidates. Otherwise, the type inferred for V is never.

If T extends U is used in a condition type? In X: Y, if the infer keyword is used in U, the type of V is a union of those candidate types if the inferred candidate types are inferred from a covariant position. Conversely, if there are candidate types inferred from contravariant positions, then the type of V is a cross type of those candidate types. Otherwise V is of type never. For a specific example, see Puzzle 3.

type Case<T> = [T] extends [(args: infer R) => any] ? R : never;
type Demo = Case<((args: { name: string; }) => any) | ((args: { age: number; }) => any)>  
Copy the code

In type Case, the position of infer R is in the parameters of the function, that is, the inverse. So the type of R is the crossover type of the candidate type. That is {name: string} & {age: number}.

summary

Do you want to do some exercises after learning? Try the following questions to see how well you understand condition types.

  1. Type Lookup
  2. Question to consider: in puzzle question 2, why cannot the type of Case be written
type Case<T> = [T] extends [(args: infer R) => any] ? R : never; type Demo = Case<((args: { name: string; }) => any) | ((args: { age: number; }) => any)> // If type <T> = T extends (args: infer R) => any? R : neverCopy the code
  1. Union To Intersection

Finally, I hope to do type gymnastics with you happily 😄