当前位置:网站首页>[typescript] type reduction (including type protection) and type predicate in typescript
[typescript] type reduction (including type protection) and type predicate in typescript
2022-07-29 05:39:00 【Underwater barbecue shop AI】
Catalog
Preface
stay TypeScript in , If a variable uses a union type , When we use this variable, it is necessary to explicitly limit the specific type of this variable , This is called Type reduction , stay TypeScript Most of the ways to reduce the size of the medium type are that we are JS Common ways in , For example, use if sentence 、type,instanceof The operator 、in Operator etc. , But there are also some ways TS Peculiar , For example, what I'm going to talk about next Type predicate
1、 Type reduction
stay 【TypeScript】TypeScript Common types ( The next part ) We mentioned type reduction in the use of joint types of , In this article, we will discuss the types in detail .
Let's look at an example :

We didn't check clearly padding Is it number , Nor did it deal with it string The situation of , here TypeScript Out of Type protection The purpose will throw errors , We can do that :
function padLeft(padding: number | string) {
if (typeof padding === "number") {
// here padding Reduced to number type
return padding + 1;
}
// here padding Reduced to string type
return padding;
}
stay if Under inspection ,TypeScript notice typeof padding ==="number" , And understand it as a special form of code , be called Type protection
Type protection It is a kind of executable runtime check expression , Used to ensure that the type is within a certain range .
Type protection Also known as Type guard 、 Type protection etc.
TypeScript Follow the execution path that our program may take , To analyze a value at a specific location Most specifically Possible types of .
It looks at these special checks ( Type protection ) And assignment , Refine the type into more specific types than declared The process go by the name of Type reduction . In many editors , We can observe these types of changes , We often do this in our examples .
Pay attention to understanding Type protection , Type reduction The meaning and connection of the two
TypeScript Several different structures can be understood :
typeof Type guard
function printAll(strs: string | string[] | null) {
if (typeof strs === "object") {
// strs Reduced to string[] | null type
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
// strs Reduced to string type
console.log(strs);
} else {
// Do something
}
}
In this example, you will find that in the first if In the branch , I mean strs Reduced to string[] | null type , Why do you say that ?
Because in JavaScript in , typeof null In fact "object" ! This is one of the unfortunate accidents in history .
Users with enough experience may not be surprised , But not everyone is JavaScript This has been the case in the past ; Fortunately, ,typescript Let us know , strs Only narrow down to string[] | null , Not just string[] , So it will definitely report an error :

We need to use It's worth shrinking Further treatment :
It's worth shrinking
stay if Values like the following in the statement :
0NaN""( An empty string )0n(bigintZero version )nullundefined
All the above values are forcibly converted to false , Other values are forced to true, stay TypeScript We can use these “ empty ” Value to do type It's worth shrinking :
function printAll(strs: string | string[] | null) {
if (strs && typeof strs === "object") {
// Add judgment str Not empty , That is, the truth value decreases
// strs Reduced to string[]
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
// strs Reduced to string type
console.log(strs);
} else {
// Do something
}
}
It should be noted that , Here is the truth value reduction statement You can't Put it on the outermost side :
function printAll(strs: string | string[] | null) {
if (strs) {
// Can not be !!!
if (typeof strs === "object") {
// ...
} else if (typeof strs === "string") {
// ...
} else {
// ... }
}
}
In this case, we may no longer correctly handle empty strings !
Equivalent reduction
typescript You can also use branch statements to do === , !== , == , and != Equivalence check , To implement type reduction . for example :
Use equivalent reduction to solve the problem that empty strings may not be handled correctly in the above truth reduction :
function printAll(strs: string | string[] | null) {
if (strs !== null) {
// Correctly from strs Remove from null .
// ...
}
}
Other examples of equivalent reduction :
function example(x: string | number, y: string | boolean) {
if (x === y) {
// x And y Exactly the same type ,x,y Are reduced to string type
x.toUpperCase();
y.toLowerCase();
} else {
// In this branch x and y The type of has not been reduced
console.log(x);
console.log(y);
}
}
function multiplyValue(container: number | null | undefined, factor: number) {
// Exclude... From the type undefined and null
if (container != null) {
console.log(container);
// Now we can safely multiply by “container.value” 了
container *= factor;
}
}
in Operator zoom out
JavaScript in in The operator is used to determine whether an object has an attribute of a certain name , stay typescript You can use it to reduce the type according to whether the type object contains a certain attribute :
type Fish = {
swim: () => void };
type Bird = {
fly: () => void };
type Human = {
swim?: () => void; fly?: () => void };
function move(animal: Fish | Bird | Human) {
if ("swim" in animal) {
// animal: Fish | Human
animal;
} else {
// animal: Bird | Human
animal;
}
}
Optional attributes will also exist on both sides of the zoom out , This is what exists in the branches above Human The reason for the type
instanceof Operator zoom out
JavaScript in instanceof Used to determine whether a variable is an instance of an object ,instanceof Operators and typeof Operators are similar , Used to identify the type of object being processed . And typeof The difference is ,instanceof Methods require developers Specifically Confirm that the object is a given type .
function logValue(x: Date | string) {
if (x instanceof Date) {
// x The type is reduced to Date
console.log(x.toUTCString());
} else {
// x The type is reduced to string
console.log(x.toUpperCase());
}
}
logValue(new Date()); // Tue, 26 Jul 2022 07:37:10 GMT
logValue("hello ts"); // HELLO TS
Allocation reduction
When we assign a value to any variable ,TypeScript The right side of the assignment will be viewed and the left side will be reduced appropriately .
let x = Math.random() < 0.5 ? 10 : "hello world!"; // let x: string | number
x = 1;
console.log(x); // After assignment , At this time x Reduce to number type
x = "Ailjx";
console.log(x); // After assignment , At this time x Reduce to string type
// Something went wrong !
x = true; // You can't type “boolean” Assign to type “string | number”.
console.log(x); // let x: string | number
Please note that , Each of these allocations is valid . Even the types observed after our first assignment x Change to number , We can still string Assign a value to x . This is because x The starting type is string | number .
Not applicable union type( Joint type )
Imagine that we are trying to encode shapes such as circles and squares . Circles record their radii , Fang recorded their side length . We will use a called kind To tell us which shape we are dealing with . Here is the definition Shape My first attempt .
interface Shape {
kind: "circle" | "square";
radius?: number;
sideLength?: number;
}
Be careful , The string used here Type of writing Union .
"circle"and"square "Tell us whether we should treat this shape as a circle or a square . By using"circle" | "square "instead ofstring, It can avoid the problem of spelling mistakes .
Write a getArea function , Apply the correct logic according to whether it deals with a circle or a square .
We first try to deal with circles :
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius! ** 2;
}
}
because radius It's an optional property , Use the bulletin directly The object may be “ Undefined ” Of , It's used here Non empty assertion operators ( ! suffix )( For details, see 【TypeScript】TypeScript Common types ( The next part ) in 6、null and undefined) To avoid reporting errors .
The above way of writing is not ideal , The type checker has no way of knowing... Based on the type attribute radius or sideLength Whether there is ( We have to use non empty assertion operators ( ! suffix )). We need to communicate what we know to the type checker . Consider this , Let's redefine Shape:
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
ad locum , We will correctly Shape There are two types , by kind Property has different values , however radius and sideLength Properties declared as required in their respective types .
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
}
}
This gets rid of ! suffix , When the union type (union type) Each type in contains a literal type Same property when ,TypeScript Think this is a different union , And can shrink union Members of .
In the example above , kind That same attribute ( This is it. Shape Of Discriminant properties ). Check kind Whether the property is "circle" , You can eliminate Shape None of them "circle" The type of the type attribute . That's all. Shape The scope of is reduced to Circle This type .
The same inspection method applies to switch sentence . Now we can try to write a complete getArea , Without any annoying non empty assertions ! suffix :
function getArea(shape: Shape) {
switch (shape.kind) {
// shape: Circle
case "circle":
return Math.PI * shape.radius ** 2;
// shape: Square
case "square":
return shape.sideLength ** 2;
}
}
This shows that the joint type sometimes does not apply .
never Type and exhaustion check
When narrowing down , You can reduce the option of a consortium to the point where you have deleted all possibilities and there is nothing left .
In these cases ,TypeScript Will use a never Type to represent a state that should not exist .
never Types can be assigned to each type ; however , No type can be assigned to never( except never In itself ). This means you can use shrink and rely on never What's new is in switch Do... In the sentence A thorough examination .
for example , Above getArea Add a default value to the function , Trying to assign shape to never , When every possible situation is not handled , It will trigger :
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
default:
// Under this branch shape both never type
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
When in Shape Add a new member to the Alliance , Will lead to TypeScript error :
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
interface Triangle {
kind: "triangle";
sideLength: number;
}
type Shape = Circle | Square | Triangle;

Control flow analysis
up to now , We have illustrated with some basic examples TypeScript How to narrow down in a specific branch . But apart from coming out of every variable , And in if 、while 、 Conditions Wait for Type protection outside , There are more things to do . for example :
function padLeft(padding: number | string) {
if (typeof padding === "number") {
// here padding Reduced to number type
return padding + 1;
}
// here padding Reduced to string type
return padding;
}
padLeft From its first if Returns... In a block .TypeScript Be able to analyze this code , And see in padding In the case of numbers , The rest of the body (return padding; ) It's not reachable . therefore , It will be able to number from padding Remove... From the type of ( from number | string To string), For the rest of the function .
such Code analysis based on accessibility go by the name of Control flow analysis ,TypeScript Use this flow analysis to narrow down the type , Because it met Type protection and assignment . When a variable is analyzed , Control flows can be split and recombined again and again , This variable can be observed to have different types at each point .
function example() {
let x: string | number | boolean;
x = Math.random() < 0.5;
// x The type is reduced to boolean
console.log(x);
if (Math.random() < 0.5) {
x = "hello";
// x The type is reduced to string
console.log(x);
} else {
x = 100;
// x The type is reduced to number
console.log(x);
}
// Be careful : above if At least one branch of the statement will execute , therefore x It can no longer be boolean type
// x The type is reduced to string | number
return x;
}
let x = example();
x = "hello";
x = 100;
x = true; // error: You can't type “boolean” Assign to type “string | number”.
2、 Type predicate
Type predicates are a way of type reduction , The reason why I mentioned it alone , It is because it is similar to what we know above JavaScript Zhongben contains in different ways .
TypeScript Type predicates in work on functions , If the function returns true, Then the type of the parameter is reduced to the type specified in the type predicate .
We first define a function that determines whether the variable is a string :
function isStr(x: string | number) {
return typeof x === "string";
}
When we use this function, we will find that it has no effect :
function toUpperCase(x: string | number) {
if (isStr(x)) {
// Report errors ,x The type of is still string | number, Not shrunk
x.toUpperCase();
}
}

At this point, you can use type predicates , Explicitly tell TypeScript, If isStr The return value of is true, Then the type of the formal parameter is a string :
function isStr(x: string | number): x is string {
return typeof x === "string";
}
function toUpperCase(x: string | number) {
if (isStr(x)) {
x.toUpperCase(); // x The type was successfully reduced to string
}
}
In this case , x is string It's our type predicate . The form of the predicate is parameterName is Type , Its :
parameterNameIt must be the parameter name in the current function signature , For example, in this caseparameterNameOnly forxTypeWhen the return value of the function istrueType of time parameter , It must be contained in the type defined by the parameter , For example, in this caseTypeNot forboolean
We can also use Type guard ( Type protection )isStr To filter the array , get string Array of :
const arr: (string | number)[] = [1, 2, "1", "2"];
const strarr: string[] = arr.filter(isStr);
// For more complex examples , This predicate may need to be reused :
const strArr: string[] = arr.filter((item): item is string => {
// Some operations
return isStr(item);
});
Use type predicates to implement a sieve throwing program safely and strictly. See the boss article :TypeScript Basics — Type predicate
Conclusion
As a front-end lover , Like sharing and Virgo's chicken , Every time I write, I will check all kinds of materials , The language expression has changed and changed , I just hope to create what everyone likes , Easy to understand , It's not a classic article , But sometimes their efforts and gains are completely out of proportion , Looking at his busy afternoon or even one day, he wrote articles with only a few hundred dozens of browsing , The mood will inevitably lose , But I don't give up , I enjoy the process of creation , Love your industry , I believe that one day gold will be found and shine brightly !
边栏推荐
- Terminal shell common commands
- Niuke network programming problem - [wy22 Fibonacci series] and [replace spaces] detailed explanation
- AR虚拟增强与现实
- [C language series] - storage of deep anatomical data in memory (II) - floating point type
- 【TypeScript】深入学习TypeScript对象类型
- Basic principle of recursion
- Day 2
- 【C语言系列】— 把同学弄糊涂的 “常量” 与 “变量”
- 第一周总结
- Basic concepts of MySQL + database system structure + extended application + basic command learning
猜你喜欢

ClickHouse学习(十)监控运行指标

Preemptive appointment | Alibaba cloud shadowless cloud application online conference appointment opens

Day 5

Detailed explanation of serial port communication

浅谈范式

Cmu15-213 shell lab experiment record

table中同一列中合并相同项

ClickHouse学习(十一)clickhouseAPI操作

One dimensional array exercise
![[C language series] - print prime numbers between 100 and 200](/img/61/5b9dea72e7a3fd450a87fe35fb94eb.png)
[C language series] - print prime numbers between 100 and 200
随机推荐
【JS题解】牛客网JS篇1-10题
ClickHouse学习(十)监控运行指标
Together with digital people, digital space and XR platform, Alibaba cloud and its partners jointly build a "new vision"
C language file operation
Clickhouse learning (VII) table query optimization
Clickhouse learning (VI) grammar optimization
Detailed explanation of GPIO input and output
Alibaba cloud Zhang Xintao: heterogeneous computing provides surging power for the digital economy
微信小程序更改属性值-setData-双向绑定-model
JS deep copy - Notes
第三课threejs全景预览房间案例
[C language series] - constants and variables that confuse students
table中同一列中合并相同项
B - 识别浮点常量问题
ClickHouse学习(一)ClickHouse?
365 day challenge leetcode 1000 questions - day 035 one question per day + two point search 13
全局components组件注册
【TypeScript】TypeScript中类型缩小(含类型保护)与类型谓词
Bubble sort c language
移动端-flex项目属性