[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 } }