当前位置: 首页 > news >正文

Typescript第七章 处理错误(返回null,抛出异常,返回异常,Option类型)

第七章 处理错误

Typescript竭尽所能,把运行时异常转移到编译时。Typescript是功能丰富的系统,加上强大的静态和符号分析能力,包揽了大量辛苦的工作。

但是有些问题是无法避免的,比如网络和文件系统异常,解析用户输入时出现额错误,堆栈溢出及内存不足。不过,Typescript的类型系统足够强大,提供了很多处理运行时错误的方式,不会眼看程序崩溃。

  • 返回null
  • 抛出异常
  • 返回异常
  • Option类型

7.1 返回null

 function ask() {return prompt("when is your birthday")}function isValid(data: Date) {return Object.prototype.toString.call(date) === '[object Date]' &&!Number.isNaN(date?.getTime())}function parse(birthday: string | null): Date | null {let date;// null判断if (birthday) {date = new Date(birthday)if (!isValid(date)) {return null}}​return new Date}let date = parse(ask())// 强制判断是否为nullif (date) {console.info("Date is ", date.toISOString());} else {console.error("Error parsing date for some reason");}​

返回null是处理错误最轻量的方式。

然而这么做丢失了一些信息,调用方不知道为什么错误。

7.2 抛出异常

把返回null改成抛出异常

 function parse(birthday:string):Date{let date = new Date(birthday)if(!isValid(date)){throw new RangeError("enter a date in the form YYYY/MM/DD")}return date}//使用时捕获错误try{let date = parse(ask())log(date.toISOString())}catch(e){if(e instanceof RangeError){log(e.message)}else{throw e}}

自定义错误类型

 class InvalidDateFormatError extends RangeError{}class DateIsInTheFutureError extends RangeError{}function parse(birthday:string):Date{let date = new Date(birthday)if(!isValid(date)){throw new InvalidDateFormatError("enter a date in the form YYYY/MM/DD")}if(date.getTime()>Date.now()){throw new DateIsInTheFutureError("are you a timelord")}return date}

7.3 返回异常

Typescript不支持throws子句。不过我们可以使用并集类型近似实现这个特性

throws子句的作用是指出一个方法可能抛出什么运行时异常,让使用方知道该处理那些异常

 ​class InvalidDateFormatError extends RangeError { }class DateIsInTheFutureError extends RangeError { }​/**@thorws{InvalidDateFormateError} xxxx**/function parse(birthday: string|null): Date | InvalidDateFormatError | DateIsInTheFutureError {let date = new Date(birthday!)if (!isValid(date)) {return new InvalidDateFormatError("enter a date in the form YYYY/MM/DD")}if (date.getTime() > Date.now()) {return new DateIsInTheFutureError("are you a timelord")}return date}​let result = parse(ask())// 手动判断可能出现的异常if(result instanceof InvalidDateFormatError){console.log("");}else if(result instanceof DateIsInTheFutureError){console.log("----");}else{console.log("ok");}

这里,我们利用Typescript的类型系统实现了

  • 在parse函数的签名中加入可能出现的异常
  • 告诉使用方可能抛出那些异常
  • 迫使使用方处理(或再次抛出)每一个异常

使用方太烂的话,可以不用逐一处理各个异常,但是要明确写出来

 if(result instanceof Error){log}else{log}

这种方式的缺点是,串联和嵌套可能会让人觉得烦躁。如果一个函数返回T|Error1,那么使用该函数的函数有两个选择:

  1. 显示处理Error1
  2. 处理T(成功的情况),把Error1传给使用方处理。这样传来传去,使用方要处理的错误将越来越多。
 function x():T|Error1{}function y():U|Error1|Error2{let a = x()if(a instanceof Error){return a}}function z():U|Error1|Error2|Error3{let a = y()if(a instanceof Error){return a}}

7.4 Option类型

除此之外,还可以使用专门的数据类型描述异常。这种方式与返回值和错误的并集相比是有缺点的(尤其是与不使用这些数据类型的代码互操作时),但是却便于串联可能出错的操作。在这方面,常用的三个选项是Try(try type),Option(也叫Maybe)和Either(either type)类型。本章只介绍Option类型,其他两个类型本质上基本相同。

注意:Try,Option,和Either与Array,Error,Map或Promise不同,不是JavaScript环境内置的数据类型。如果想使用,要在NPM中寻找实现,或者自己编辑

Option类型源自Haskell,OCaml,Scala和Rush等语言,隐含的意思是,不返回一个值,而是返回一个容器,该容器里可能有一个值,也可能没有。 这个容器有一些方法,即使没有值也能串联操作。容器几乎可以是任何数据结构,只要能在里面存放值。例如,可以使用数组作为容器:

 function parse(birthday: string | null): Date[] {if (birthday) {let date = new Date(birthday)if (!isValid(date)) {return []}return [date]}return []}function isValid(date: Date) {return Object.prototype.toString.call(date) === '[object Date]' &&!Number.isNaN(date?.getTime())}let ask = (): string | null => {let number = Math.random();if (number > 0.5) {return "2023/08/01"} else {return null}}let date = parse(ask())date.map(_=>_.toISOString()).forEach(_=>console.log("date is",_))​

你可能注意到了,Option的缺点与返回null一样,只告诉使用方什么地方出错了,而未说明出错的原因。

Option真正发挥作用是在一次执行多个操作,而每个操作都有可能出错。

例如,我们之前一定假定ask一定成功,而parse可能失败。但是如果ask失败了,我们不能放置不理,继续计算,此时,仍然可以使用Option。

 let ask = () => {let result = "2023/08/01"if (result === null) {return []}return [result]​}ask().map(parse).map(date=>date.toISOString())// 报错.forEach(date=>console.log("date is",date))

出错了。这是因为我们把一个Date数组(Date[])映射到了一个Date数组构成的数组上(Date[][]),解决方法是在操作之前整平数组:

 let ask = () => {let result = "2023/08/01"if (result === null) {return []}return [result]​}flatten(ask().map(parse)).map(date=>date.toISOString())// 报错.forEach(date=>console.log("date is",date))​function flatten<T>(array:T[][]):T[]{return Array.prototype.concat.apply([],array)}

这样有点不便,Option类型没有告诉我们什么信息(一切都是常规数组)。为了改变这种局面,我们可以把整个过程(把一个值放入一个容器,提供操作那个值的方式,提供从容器中取回结果的方式)包装成一个特殊的数据结构,让一切一目了然。实现好以后,可以像下面这样使用

 ask().flatMap(parse).flatMap(date => new Some(date.toISOString())).flatMap(date => new Some("date si "+date)).getOrElse("Error parsing date for some reason")

我们将才用下述方式定义Option类型:

  • Option是一个接口,实现两个类:Some和None,这是两个Optioin。Some是包含一个T类型值的Option,None是没有值的Option,表示失败。
  • Option即是类型也是函数。作为类型,他是一个接口,表示Some和None的超类型。作为函数,他是创建Option类型值的方式。
 interface Option<T>{} //1.class Some<T> implements Option<T> {//2.constructor(private value:T){}}class None implements Option<never>{}//3
  1. Option是一个接口,Some和None都可以实现该接口
  2. Some表示操作成功,得到一个值。与前面使用的数组一样,Some是改值的容器。
  3. None表示失败,不包括值。

这几个类型等效于下述通过数组实现的Option:

  • Option是[T]|[]
  • Some是[T]
  • None是[]

我们能对Option做些什么呢,就目前

flatMap

串联操作可能为空的Option

getOrElse

从Option取得值

首先在Option接口中定义这两个操作,然后在Some和None中具体实现:

 interface Options<T> {flatMap<U>(f: (value: T) => None): NoneflatMap<U>(f: (value: T) => Options<U>): Options<U>getOrElse(value: T): T;}class Some<T> implements Options<T> {constructor(private value: T) {}flatMap<U>(f: (value: T) => None): NoneflatMap<U>(f: (value: T) => Some<U>): Some<U>flatMap<U>(f: (value: T) => Options<U>): Options<U> {return f(this.value);}getOrElse(): T {return this.value;}}class None implements Options<never> {flatMap(): None {return this;}getOrElse<U>(value: U): U {return value;}}function Options<T>(value:null|undefined):Nonefunction Options<T>(value:T):Some<T>function Options<T>(value:T):Options<T>{if(value == null){return new None}return new Some(value)}​
 ask().flatMap(parse).flatMap(date => new Some(date.toISOString()))// Options<string>.flatMap(date => new Some("date si " + date))// Options<string>.getOrElse("Error parsing date for some reason")// string​// let result = Options(6)// .flatMap(n=>Options(n*3))// .flatMap(n=>new None)// .getOrElse(7)

Option也不是没有缺点,Option通过一个None表示失败,没有关于失败的详细信息,也不知道失败的原因。另外与不使用Option的代码无法互操作(要自己手动包装API,让他们返回Option)

相关文章:

Typescript第七章 处理错误(返回null,抛出异常,返回异常,Option类型)

第七章 处理错误 Typescript竭尽所能&#xff0c;把运行时异常转移到编译时。Typescript是功能丰富的系统&#xff0c;加上强大的静态和符号分析能力&#xff0c;包揽了大量辛苦的工作。 但是有些问题是无法避免的&#xff0c;比如网络和文件系统异常&#xff0c;解析用户输入…...

Qt库xcb问题

首先在~/.bashrc中加入 export QT_DEBUG_PLUGINS1然后看具体的报错 查看某个库链接的库&#xff1a; ldd libqxcb.so然后找到真正缺少的库&#xff0c;再在路径下搜索&#xff0c;然后建立软链接。 https://blog.csdn.net/LOVEmy134611/article/details/107212845 https://…...

C++ | 哈希表的实现与unordered_set/unordered_map的封装

目录 前言 一、哈希 1、哈希的概念 2、哈希函数 &#xff08;1&#xff09;直接定址法 &#xff08;2&#xff09;除留余数法 &#xff08;3&#xff09;平方取中法&#xff08;了解&#xff09; &#xff08;4&#xff09;随机数法&#xff08;了解&#xff09; 3、哈…...

【漏洞挖掘】Xray+rad自动化批量漏洞挖掘

文章目录 前言一、挖掘方法二、使用步骤工具安装使用方法开始挖掘 总结 前言 自动化漏洞挖掘是指利用计算机程序和工具来扫描、分析和检测应用程序、网络和系统中的安全漏洞的过程。这种方法可以帮助安全专家和研究人员更高效地发现和修复潜在的安全威胁&#xff0c;从而提高整…...

Swagger UI教程 API 文档和Node的使用

在团队开发中&#xff0c;一个好的 API 文档可以减少很多交流成本&#xff0c;也可以使一个新人快速上手业务。 前言 swagger ui是一个API在线文档生成和测试的利器&#xff0c;目前发现最好用的。为什么好用&#xff1f;Demo 传送门 支持API自动生成同步的在线文档 这些文档可…...

P5691 [NOI2001] 方程的解数

[NOI2001] 方程的解数 题目描述 已知一个 n n n 元高次方程&#xff1a; ∑ i 1 n k i x i p i 0 \sum\limits_{i1}^n k_ix_i^{p_i} 0 i1∑n​ki​xipi​​0 其中&#xff1a; x 1 , x 2 , … , x n x_1, x_2, \dots ,x_n x1​,x2​,…,xn​ 是未知数&#xff0c; k 1 ,…...

rust里用什么表示字节类型?

在Rust中&#xff0c;字节可以使用 u8 类型来表示。 u8 是一个无符号8位整数类型&#xff0c;可以表示0到255之间的值&#xff0c;对应于一个字节的范围。 以下是一个示例&#xff0c;演示了如何声明和使用字节&#xff1a; fn main() {let byte: u8 65; // 表示字母A的ASCI…...

CMake简介

文章目录 为什么需要头文件为什么 C 需要声明头文件 - 批量插入几行代码的硬核方式头文件进阶 - 递归地使用头文件 CMake什么是编译器多文件编译与链接CMake 的命令行调用为什么需要库&#xff08;library&#xff09;CMake 中的静态库与动态库CMake 中的子模块子模块的头文件如…...

[threejs]相机与坐标

搞清相机和坐标的关系在threejs初期很重要&#xff0c;否则有可能会出现写了代码&#xff0c;运行时一片漆黑的现象&#xff0c;这种情况就有可能是因为你相机没弄对。 先来看一下threejs中的坐标(世界坐标) 坐标轴好理解&#xff0c;大家只需要知道在three中不同颜色代表的轴…...

Qt信号与槽机制的基石-MOC详解

引入 上篇讲到了信号与槽就是实现的观察者模式&#xff0c;那具体如何生成映射表就是moc做的事情。 一、moc简介 1. moc的定义 moc 全称是 Meta-Object Compiler&#xff0c;也就是“元对象编译器”&#xff0c;它主要用于处理C源文件中的非标准C代码。Qt 程序在交由标准编…...

关于单体架构缓存刷新实现方案

背景 如果各位看官是分布式项目应该都采用分布式缓存了&#xff0c;例如redis等&#xff0c;分布式缓存不在本次讨论范围哈&#xff1b;我个人建议是&#xff0c;如果是用户量比较大&#xff0c;建议采用分布式缓存机制&#xff0c;后期可以很容易前后到分布式服务或微服务。 …...

洞悉安全现状,建设网络安全防护新体系

一、“网络攻防演练行动“介绍 国家在2016年发布《网络安全法》&#xff0c;出台网络安全攻防演练相关规定&#xff1a;关键信息基础设施的运营者应“制定网络安全事件应急预案&#xff0c;并定期进行演练”。同年“实战化网络攻防演练行动”成为惯例。由公安部牵头&#xff0…...

spring中怎么通过静态工厂和动态工厂获取对象以及怎么通过 FactoryBean 获取对象

&#x1f600;前言 本章是spring基于XML 配置bean系类中第4篇讲解spring中怎么通过静态工厂和动态工厂获取对象以及怎么通过 FactoryBean 获取对象 &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望…...

三元组表实现矩阵相加(数据结构)

代码&#xff1a; 含注释&#xff0c;供参考 #include <stdio.h> #include <stdlib.h>typedef struct {int row,col,value;//分别为行数&#xff0c;列数&#xff0c;数值 } Triple; typedef struct {int len;//非零数值的个数Triple data[200]; } TSMatrix;void…...

ChinaJoy 2023微星雷鸟17游戏本震撼发布:搭载AMD锐龙9 7945HX首发8499元

ChinaJoy 2023展会中微星笔记本再次给大家带来惊喜&#xff0c;发布了搭载AMD移动端16大核的旗舰游戏本&#xff1a;雷鸟17&#xff0c;更重要的这样一款旗舰性能的游戏本&#xff0c;首发价8499元堪称当今游戏本市场中的“性价比爆款”&#xff01; 本着和玩家一同制霸游戏战场…...

各种运算符

算术运算符 1.双目运算符 */%&#xff1a;从左到右优先级依次降低 一些注意事项&#xff1a; 1若a/b都为整型那么结果也为整型&#xff0c;如果ab其中有一个为实型&#xff0c;结果则为实型 求余运算符注意事项&#xff1a; 1运算对象必须为整数 2运算结果的整数跟左边数字的…...

yolov3-tiny原理解析及代码分析

前言 从去年十一月份开始学习yolo神经网络用于目标识别的硬件实现&#xff0c;到现在已经六个月了。一个硬件工程师&#xff0c;C/C基础都差劲的很&#xff0c;对照着darknet作者的源码和网上东拼西凑的原理讲解&#xff0c;一点一点地摸索。刚开始进度很慢&#xff0c;每天都…...

深入了解Redis-实战篇-短信登录

深入了解Redis-实战篇-短信登录 一、故事背景二、知识点主要构成2.1、短信登录2.1.1、生成随机短信验证码引入maven依赖生成验证码 2.1.2、实现登录校验拦截器2.1.3、基于Redis实现短信登录2.1.3.1、发送验证码时存入Redis2.1.3.2、登录时校验验证码 2.1.4、解决状态登录刷新的…...

Mysql的锁

加锁的目的 对数据加锁是为了解决事务的隔离性问题&#xff0c;让事务之前相互不影响&#xff0c;每个事务进行操作的时候都必须先加上一把锁&#xff0c;防止其他事务同时操作数据。 事务的属性 &#xff08;ACID&#xff09; 原子性 一致性 隔离性 持久性 事务的隔离级别 锁…...

【EI/SCOPUS征稿】2023年算法、图像处理与机器视觉国际学术会议(AIPMV2023)

2023年算法、图像处理与机器视觉国际学术会议&#xff08;AIPMV2023&#xff09; 2023 International Conference on Algorithm, Image Processing and Machine Vision&#xff08;AIPMV2023&#xff09; 2023年算法、图像处理与机器视觉国际学术会议&#xff08;AIPMV2023&am…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

云原生周刊:k0s 成为 CNCF 沙箱项目

开源项目推荐 HAMi HAMi&#xff08;原名 k8s‑vGPU‑scheduler&#xff09;是一款 CNCF Sandbox 级别的开源 K8s 中间件&#xff0c;通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度&#xff0c;为容器提供统一接口&#xff0c;实现细粒度资源配额…...