[TypeScript 4.5] 004 - Chapter 4 type reduction

[TypeScript 4.5] 004 - Chapter 4 type reduction

1, typeof type guard

1. What is type reduction

meaning

TypeScript type reduction is the process of converting from wide type to narrow type

Type reduction is often used in scenarios that deal with union type variables

code analysis

function padLeft(padding: number | string, input: string): string {
    return new Array(padding + 1).join(" ") + input // Error: operator "+" cannot be applied to types "string | number" and "number".
}

2. Code transformation using typeof

Modified code

function padLeft(padding: number | string, input: string): string {
    if(typeof padding === "number") {
        return new Array(padding + 1).join(" ") + input
    }
    return padding + input
}
console.log(100, "Ha ha ha")
console.log("eldest brother", "Liu Bei")

results of enforcement

PS D:\MyFile\VSCodeProjects\study-ts\Chapter 4 type reduction\dist> node .\01-typeof.js
100 Ha ha ha
 Big brother Liu Bei

3. typeof type guard

summary

Returns a string representation of the current type.

Use example

typeof a === "object"
// Besides object, there are string, number, bigint, boolean, symbol, undefined and function

Problem code example

function printAll(strs: string | string[] | null): string {
    // It should be noted that when the value of strs is of string [] type, it returns "object"
    // When the value of strs is null, "object" is returned
}

2, Truth reduction

1. Overview

explain

Truth checking is a common thing in JavaScript

We can use conditions, & &, |, Boolean negation (!) To check the truth

code analysis

function getUserOnlineMessage (numUsersOnline: number) {
    if (numUsersOnline) { // If the values of numUsersOnline here are 0, NaN, "" "(empty string), On (bigint zero version), null and undefined, it is false
        return `Now shared ${numUsersOnline} People online!`
    }
    return "No one is online now!"
}
// The following two results return true, and the value is consistent with the judgment standard of the value in the if() bracket!
Boolean("hello")
!!"world" // One! Convert it to text type, one! Convert it to boolean type!

2. Solve problems in typeof type guard

function printAll(strs: string | string[] | null) {
    // To judge whether strs is string [] and not null, you can write it like this!
    if(strs && typeof strs === "object"){
        // ...
    }
}

3, Equivalent reduction

1. Explain

TypeScript can also use branch statements to do congruent (=), all unequal (!) Equal to (= =) and not equal to (! =) to check the equivalence and reduce the type.

2. Code example

It's too simple! I don't want to write code examples!

// Congruent
function doSth(str1: string | null, str2: string | number) {
    if (str1 === str2) {
        str1.toUpperCase()
        str2.toLowerCase()
    }
}

// Unequal (this should be noted)
function doSth1(value: number | null | undefined, x: number) {
    if(value != null){ // Replace null here with undefined, and the result is the same!
        value *= x
        console.log(value)
    }
}
doSth1(100, 5) // 500
doSth1(null, 5) // Print nothing
doSth1(undefined, 5) // Note: This is also nothing to print! It has been filtered automatically!

4, in operator zoom out

1. Overview

explain

It is used to determine whether the operator in has a JavaScript attribute!

code analysis

// format
value in X
// value is a string (representing the property name)
// If the result is true, X is required to have a value of the type of optional or required attribute
// If the result is false, X is required to have a value of the type of optional or missing attribute

2. Code demonstration

Code example and explanation

If the result is true, X is required to have the value of optional or required attribute type!

type Fish = {
    swim: () => void
}
type Bird = {
    fly: () => void
}
function move(animal: Fish | Bird){
    if("swim" in animal){
        return animal.swim
    }
    return animal.fly
}

Add a People type and make it have both (optional) properties!

type Fish = {
    swim: () => void
}
type Bird = {
    fly: () => void
}
type People = {
    swim?: () => void,
    fly?: () => void
}
function move(animal: Fish | Bird | People){
    if("swim" in animal){
        // animal: Fish | People
        // We can assert it as Fish
        return (animal as Fish).swim
    }
    // animal: Bird | People
    // We can assert it as Bird
    return (animal as Bird).fly
}

It can also be executed without assertion when it has this method!

type Fish = {
    swim: () => void
}
type Bird = {
    fly: () => void
}
type People = {
    swim?: () => void,
    fly?: () => void
}
function move(animal: Fish | Bird | People) {
    if ("swim" in animal) {
        // animal: Fish | People
        return animal?.swim
    }
    // animal: Bird | People
    return animal?.fly
}

5, instanceof operator

1. Overview

explain

JavaScript uses the instanceof operator to check whether a value is an instance of another value

instanceof is also a type protection

TypeScript implements type reduction in branches protected by instanceof

code analysis

X instanceof Foo // Used to check whether the prototype chain of X contains foo prototype

2. Code demonstration

Code example

function logValue(x: Date | string) {
    if (x instanceof Date) {
        console.log(x.toUTCString())
    } else {
        console.log(x.toUpperCase())
    }
}
logValue(new Date())
logValue("hello world")

results of enforcement

PS D:\MyFile\VSCodeProjects\study-ts\Chapter 4 type reduction\dist> node .\05-instanceof.js
Mon, 07 Feb 2022 01:08:07 GMT
HELLO WORLD

6, Allocation reduction

1. Overview

explain

When we assign a value to any variable

TypeScript looks to the right of the assignment

And reduce the left side appropriately

Code example

// let x: string | number
let x = Math.random() < 0.5 ? 10 : "hello world"

2. Demo code

Code example and explanation

// let x: string | number (automatically inferred as union type)
let x = Math.random() < 0.5 ? 10 : "hello world"
// let x: number
x = 1
console.log(typeof x)
console.log(x)
// let x: string
x = "good morning"
console.log(typeof x)
console.log(x)
// Manual assignment test
let y: number | string
y = 100
console.log(typeof y)
console.log(y)
y = "good morning"
console.log(typeof y)
console.log(y)
// Conclusion: it is consistent with the automatic inference result of allocation!

results of enforcement

Here it becomes a specific type instead of a joint type. Please refer to the following control flow analysis!

PS D:\MyFile\VSCodeProjects\study-ts\Chapter 4 type reduction\dist> node .\06-assignment.js
number
1
string
good morning
number
100
string
good morning

7, Control flow analysis

1. Overview

explain

Code analysis based on accessibility, that is, control flow analysis! See code example!

Code example

function padLeft(padding: number | string, input: string) {
    if (typeof padding === "number") {
        return new Array(padding + 1).join(" ") + input
    }
    return padding + input
}

2. Code demonstration

Code example and explanation

function test() {
    let x: number | string | boolean
    x = Math.random() < 100
    // let x: boolean
    console.log(typeof x)
    console.log(x)
    if(Math.random() < 100) {
        x = "hello world"
        // let x: string
        console.log(typeof x)
        console.log(x)
    } else {
        x = 100
        // let x: number
        console.log(typeof x)
        console.log(x)
    }
    return x
}
let q = test()
q = 100
q = "hi"
// q = true / / an error is reported: the type "boolean" cannot be assigned to the type "string | number".
// Note here, the value of x in the function is inferred as string | number. The following judgment overrides the boolean type above!
console.log(typeof q)
console.log(q)

results of enforcement

PS D:\MyFile\VSCodeProjects\study-ts\Chapter 4 type reduction\dist> node .\07-ctrl.js      
boolean
true
string
hello world
string
hi

8, Using type predicates

1. Overview

explain

Sometimes we want to directly control the type change of the whole code

To define a user-defined type protection

We only need to define one function

And make its return value a type predicate

Code example

Pet is Fish is the so-called type predicate, which means that if there is a swim attribute in pet, pet is Fish type! Format: parameter name is type

function isFish (pet: Fish | Bird): pet is Fish {
    return (pet as Fish).swim !== undefined
}

2. Code demonstration

Code example and explanation

type LiuBei = {
    name: string,
    fight: boolean
}
type ZiBo = {
    name: string,
    code: boolean
}
function isMe(people: LiuBei | ZiBo): people is ZiBo {
    return (people as ZiBo).name === "Zi Bo" && (people as ZiBo).code === true
}
function getPeople(): ZiBo | LiuBei {
    if(Math.random() > 0.5){
        return { name: "Zi Bo", code: true }
    } else {
        return { name: "Liu Bei", fight: true }
    }
}
let people = getPeople()
// At this time, if isMe(people) returns true, TypeScript will know that the type of me is ZiBO, and vice versa is LiuBei
if(isMe(people)){
    console.log(people.name)
    console.log(people.code)
} else {
    console.log(people.name)
    console.log(people.fight)
}
// The following is the use of depth
const somePeople: (ZiBo | LiuBei)[] = [getPeople(), getPeople(), getPeople(), getPeople(), getPeople()]
// Filter out ZiBo. The following two lines of code are equivalent!
const zibo1: ZiBo[] = somePeople.filter(isMe)
const ZIBO2: ZiBo[] = somePeople.filter(isMe) as ZiBo[]
// Write more complicated!
const zibo3: ZiBo[] = somePeople.filter((people): people is ZiBo => { // people must have parentheses
    if (people.name === "Liu Bei"){
        return false
    }
    return isMe(people)
})

results of enforcement

PS D:\MyFile\VSCodeProjects\study-ts\Chapter 4 type reduction\dist> node .\09-type.js
 Liu Bei
true
[ { name: 'Zi Bo', code: true }, { name: 'Zi Bo', code: true } ]
[ { name: 'Zi Bo', code: true }, { name: 'Zi Bo', code: true } ]
[ { name: 'Zi Bo', code: true }, { name: 'Zi Bo', code: true } ]

9, Discriminated unions

1. Overview

Union is the union type

We've been using simple types to narrow down individual variables

But most JavaScript deals with slightly more complex structures

2. Code demonstration

discover problems

// Round and square
interface Shape {
    kind: "circle" | "square",
    radius?: number,
    sideLength?: number
}
// Calculate the area of a circle
function getArea (shape: Shape) {
    if(shape.kind === "circle"){
        // [question] this seems ideal, but there is no guarantee that radius must be defined when kind is circle!
        return Math.PI * shape.radius! ** 2
    }
}

solve the problem

// Round and square
interface Circle {
    kind: "circle",
    radius: number
}
interface Square {
    kind: "square",
    sideLength: number
}
type Shape = Circle | Square
// Calculate the area of a circle
function getArea (shape: Shape) {
    if(shape.kind === "circle"){
        return Math.PI * shape.radius ** 2
    }
}
console.log(getArea({kind: "circle", radius: 3}))

results of enforcement

PS D:\MyFile\VSCodeProjects\study-ts\Chapter 4 type reduction\dist> node .\010-unions.js
28.274333882308138

10, never type and exhaustive check

1. Overview

When narrowing the scope

We can reduce the options of the consortium

Until all possibilities are deleted

At this time, we use the never type representation

The never type indicates a state that should not exist

never can be assigned to any type

But no type can be assigned to never

Except never itself

2. Code example and explanation

// Circle, square, triangle
interface Circle {
    kind: "circle",
    radius: number
}
interface Square {
    kind: "square",
    sideLength: number
}
interface Triangle {
    kind: "triangle",
    sideLength: number
}
type Shape = Circle | Square | Triangle
// Calculate the area of a circle
function getArea (shape: Shape) {
    switch(shape.kind){
        case "circle":
            return Math.PI * shape.radius ** 2
        case "square":
            return shape.sideLength ** 2
        default:
            // Error: type "Triangle" cannot be assigned to type "never".
            // Reporting an error means that it is still possible, so an exhaustive check has been done!
            const _exhaustiveCheck: never = shape
            return _exhaustiveCheck
    }
}

Keywords: Javascript Front-end TypeScript

Added by phpvolution on Fri, 11 Feb 2022 11:41:18 +0200