php是做网站还是网页,网站开发前途,凡科官网登录,网站维护的方法第七章 处理错误
Typescript竭尽所能#xff0c;把运行时异常转移到编译时。Typescript是功能丰富的系统#xff0c;加上强大的静态和符号分析能力#xff0c;包揽了大量辛苦的工作。
但是有些问题是无法避免的#xff0c;比如网络和文件系统异常#xff0c;解析用户输入…第七章 处理错误
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,那么使用该函数的函数有两个选择
显示处理Error1处理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与ArrayErrorMap或Promise不同不是JavaScript环境内置的数据类型。如果想使用要在NPM中寻找实现或者自己编辑
Option类型源自HaskellOCaml,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/01if (result null) {return []}return [result]}ask().map(parse).map(datedate.toISOString())// 报错.forEach(dateconsole.log(date is,date))出错了。这是因为我们把一个Date数组Date[]映射到了一个Date数组构成的数组上Date[][],解决方法是在操作之前整平数组 let ask () {let result 2023/08/01if (result null) {return []}return [result]}flatten(ask().map(parse)).map(datedate.toISOString())// 报错.forEach(dateconsole.log(date is,date))function flattenT(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类型值的OptionNone是没有值的Option表示失败。Option即是类型也是函数。作为类型他是一个接口表示Some和None的超类型。作为函数他是创建Option类型值的方式。 interface OptionT{} //1.class SomeT implements OptionT {//2.constructor(private value:T){}}class None implements Optionnever{}//3Option是一个接口Some和None都可以实现该接口Some表示操作成功得到一个值。与前面使用的数组一样Some是改值的容器。None表示失败不包括值。
这几个类型等效于下述通过数组实现的Option:
Option是[T]|[]Some是[T]None是[]
我们能对Option做些什么呢,就目前
flatMap
串联操作可能为空的Option
getOrElse
从Option取得值
首先在Option接口中定义这两个操作然后在Some和None中具体实现 interface OptionsT {flatMapU(f: (value: T) None): NoneflatMapU(f: (value: T) OptionsU): OptionsUgetOrElse(value: T): T;}class SomeT implements OptionsT {constructor(private value: T) {}flatMapU(f: (value: T) None): NoneflatMapU(f: (value: T) SomeU): SomeUflatMapU(f: (value: T) OptionsU): OptionsU {return f(this.value);}getOrElse(): T {return this.value;}}class None implements Optionsnever {flatMap(): None {return this;}getOrElseU(value: U): U {return value;}}function OptionsT(value:null|undefined):Nonefunction OptionsT(value:T):SomeTfunction OptionsT(value:T):OptionsT{if(value null){return new None}return new Some(value)}ask().flatMap(parse).flatMap(date new Some(date.toISOString()))// Optionsstring.flatMap(date new Some(date si date))// Optionsstring.getOrElse(Error parsing date for some reason)// string// let result Options(6)// .flatMap(nOptions(n*3))// .flatMap(nnew None)// .getOrElse(7)Option也不是没有缺点Option通过一个None表示失败没有关于失败的详细信息也不知道失败的原因。另外与不使用Option的代码无法互操作要自己手动包装API让他们返回Option