当前位置: 首页 > Web前端 > HTML5

TypeScript基础介绍

时间:2023-04-05 17:14:19 HTML5

前言TypeScript的官方文档已经更新,但我能找到的中文文档还是旧版本。因此,对一些新增和修改的章节进行了翻译和整理。本文整理自TypeScriptHandbook中的“TheBasics”一章。本文并没有严格按照原文翻译,部分内容也做了说明和补充。执行不同操作时,JavaScript文字的每个值都有不同的行为。这听起来有点抽象,所以让我们举个例子,假设我们有一个名为message的变量,想象一下我们可以做什么://访问'message'上的属性'toLowerCase'//然后调用它//调用'消息'message();第一行代码是获取属性toLowerCase,然后调用它。第二行代码直接调用message。但实际上,我们连message的值都不知道,自然也不知道这段代码的执行结果。每一个行动首先取决于我们拥有什么价值。消息是可调用的吗?message是否有一个名为toLowerCase的属性?如果是这样,可以调用toLowerCase吗?如果可以调用这些值中的任何一个,它们将返回什么?当我们编写JavaScript时,我们需要牢记这些问题的答案,同时也期望处理好所有的细节。假设message是这样定义的:constmessage="HelloWorld!";你完全可以猜到这段代码的结果,如果我们尝试运行message.toLowerCase(),我们可以得到这个字符的小写版本。第二段代码呢?如果你熟悉JavaScript,你一定知道会报如下错误:TypeError:messageisnotafunction如果能避免这样的错误就好了。当我们运行代码时,JavaScript在运行时首先会弄清楚值的类型(type),然后再决定要做什么。所谓价值的类型还包括这个价值的行为和能力。当然,TypeError也会隐含地告诉我们一些事情。例如,在这个例子中,它告诉我们字符串HelloWorld不能作为函数调用。对于一些值,比如基本值string和number,我们可以使用typeof运算符来判断它们的类型。但是对于函数等其他函数,并没有相应的方法来确认它们的类型。例如,考虑这个函数:functionfn(x){returnx.flip();}我们通过阅读代码可以知道,该函数只有在向其传递一个具有可调用翻转属性的对象时才会正常执行。但是JavaScript在执行代码时不会反映这些信息。在JavaScript中,要知道传递特定值时fn发生了什么,唯一的方法是调用它并查看发生了什么。这种行为使得很难在代码运行之前预测代码将做什么,这意味着在编写代码时更难知道代码会发生什么。从这个角度来看,类型就是描述什么样的值可以传递给fn,什么样的值会导致崩溃。JavaScript仅提供动态类型,这需要您运行代码并查看会发生什么。另一种方法是使用静态类型系统,它可以在运行前预测需要哪些代码。静态类型检查(Statictype-checking)让我们回想一下将string作为函数调用产生的TypeError。大多数人不喜欢在运行代码时出现错误。这些被认为是错误。当我们编写新代码时,我们也尽量不引入新的错误。如果我们添加一点代码,保存文件,重新运行代码,我们可以立即看到错误,我们可以快速定位问题,但情况并非总是如此。例如,如果我们没有做足够的测试,我们会遇到Lessthan可能出错的地方。或者,如果我们足够幸运地看到了这个错误,我们可能不得不进行一次大的重构并添加很多不同的代码来找出问题所在。理想情况下,我们应该有一个工具可以帮助我们在代码运行之前发现错误。这就是像TypeScript这样的静态类型检查器所做的。静态类型系统描述了值应该具有的结构和行为。像TypeScript这样的类型检查器会利用这些信息,并在可能出现问题时告诉我们:constmessage="hello!";message();//此表达式不可调用。//类型“String”没有调用签名。在此示例中,TypeScript将在运行之前抛出一条错误消息。非异常失败(Non-exceptionfailure)到目前为止,我们讨论的都是运行时错误。所谓运行时错误就是JavaScript会在运行时告诉我们它认为没有意义。这些事情的发生是因为ECMAScript规范清楚地说明了这些异常的行为。例如,规范规定在调用不可调用的对象时应抛出错误。这听起来似乎很简单,所以你可能会认为获取一个不存在的对象的属性应该会抛出错误,但JavaScript不会这样做,它不会报错,并返回值不明确的。constuser={name:"Daniel",age:26,};user.location;//returnsundefined静态类型需要标记哪些代码是错误的,即使实际生效的JavaScript并没有立即报错。在TypeScript中,以下代码会产生位置不存在的错误:constuser={name:"Daniel",age:26,};user.location;//属性'location'在类型'{name:string;上不存在年龄:数字;}'。虽然有时候这意味着你在表达的时候需要做一些取舍,目的是为了在我们的项目中找到一些合理的错误。TypeScript已经捕获了很多合理的错误。例如,如错别字:constannouncement="HelloWorld!";//你能多快发现拼写错误?announcement.toLocaleLowercase();announcement.toLocalLowercase();//我们可能打算写这个...announcement.toLocaleLowerCase();Functionnotcalled:functionflipCoin(){//MeanttobeMath.random()returnMath.random<0.5;//Operator'<'cannotbeappliedtotypes'()=>number'和'number'。}基本逻辑错误:constvalue=Math.random()<0.5?“a”:“b”;if(value!=="a"){//...}elseif(value==="b"){//由于类型为'"a"'和'"b"'没有重叠。//Oops,unreachable}TypesforTooling)TypeScript不仅能在我们犯错时发现错误,还能防止我们犯错。由于类型信息,类型检查器可以检查例如是否正确获取了变量的属性。正是因为这些信息,它还可以列出您在键入时可能想要使用的属性。这意味着TypeScript对你编写代码也很有帮助,核心类型检查器不仅可以提供错误信息,还可以提供代码补全。这就是TypeScript在工具方面所做的。TypeScript非常强大,除了在您键入时提供完成和错误消息之外。它还可以支持“快速修复”功能,即自动修复错误并将其重构为组织清晰的代码。它还支持导航功能,例如跳转到定义变量的位置,或查找对给定变量的所有引用。所有这些功能都建立在类型检查器之上,并且跨平台支持。您最喜欢的编辑器很可能已经支持TypeScript。tscTypeScript编译器(tsc,TypeScript编译器)到目前为止,我们只讨论了类型检查器,但还没有使用它们。现在让我们认识一下我们的新朋友tsc——TypeScript编译器。首先,我们可以通过npm安装它:npminstall-gtypescript这将全局安装TypeScript编译器,如果你想在本地node_modules安装tsc,你也可以使用npx或类似的工具。让我们创建一个空文件夹并编写我们的第一个TypeScript程序:hello.ts://Greetstheworld.console.log("Helloworld!");注意这里没有多余的修饰,这个helloworld项目就跟你用JavaScript写的一样。现在您可以运行tsc命令来执行类型检查:tschello.ts现在我们运行了tsc,但是您会发现没有任何反应。的确,由于这里没有类型错误,所以命令行上不会有任何输出。但是如果我们再次检查,我们会发现我们得到了一个新文件。查看当前目录,我们会发现hello.ts在hello.ts的同一目录下还有一个hello.js,就是hello.ts文件编译输出的文件,而tsc会把ts文件编译成一个纯JavaScript文件。让我们看一下编译后的输出文件://Greetstheworld.console.log("Helloworld!");在这个例子中,由于TypeScript没有要编译和处理的??东西,所以看起来我们写的一样。编译器会尽量输出干净的代码,就像普通开发者写的一样。当然,这不是一件容易的事,但TypeScript会坚持这样做,比如保持缩进、注意跨行代码、保留注释等。如果我们坚持生成类型检查错误怎么办?我们可以写hello.ts://这是一个工业级的通用欢迎函数:functiongreet(person,date){console.log(`Hello${person},todayis${date}!`);}问候(“布伦丹”);现在让我们再次运行tschello.ts。这次我们会在命令行上得到一个错误:预期有2个参数,但得到了1个。TypeScript告诉我们,我们向greet函数传递的参数少了一个。虽然我们写的是标准的JavaScript,但TypeScript依然可以帮我们发现代码中的错误,酷~。EmittingwithErrors在刚才的例子中,有一个细节你可能没有注意到,那就是如果我们打开编译好的输出文件,会发现文件还是有变化的。这不是有点奇怪吗?tsc已经报错了,为什么还要重新编译文件呢?这就引出了TypeScript的一个核心观点:大多数时候,你需要比TypeScript更了解你的代码。例如,如果您正在将代码迁移到TypeScript,这会产生很多类型检查错误,并且您必须为类型检查器处理所有错误,那么您必须考虑,显然以前的代码可以正常工作,为什么TypeScript会阻止代码正常运行?所以TypeScript不会阻碍你。当然,如果你想让TypeScript更严格,你可以使用noEmitOnError编译选项,尝试更改你的hello.ts文件,然后运行??tsc:tsc--noEmitOnErrorhello.ts你会发现hello.ts并没有得到更新。显式类型到现在为止,我们还没有告诉TypeScriptperson和date是什么类型,所以让我们编辑代码来告诉TypeScriptperson是一个字符串,date是一个Date对象。同时我们使用date的toDateString()方法。functiongreet(person:string,date:Date){console.log(`Hello${person},todayis${date.toDateString()}!`);}我们所做的就是给person和date注解添加类型(typeannotations)描述了greet函数可以支持哪些值。你可以这样理解这个签名:greet支持传入一个字符串类型的人和一个Date类型的日期。添加类型注解后,TypeScript可以提示我们,比如greet调用错误:)}!`);}greet("Maddison",Date());//'string'类型的参数不能分配给'Date'类型的参数。TypeScript提示第二个参数有错误,这是为什么呢?这是因为在JavaScript中调用Date()会返回一个字符串。使用newDate()生成Date类型值。让我们快速解决这个问题:functiongreet(person:string,date:Date){console.log(`Hello${person},今天是${date.toDateString()}!`);}greet("Maddison",new日期());请记住,我们并不总是需要编写类型注释,大多数时候,TypeScript可以自动推断出类型:letmsg="hellothere!";//letmsg:string尽管我们没有告诉TypeScriptmsg是一个字符串类型的值,但它仍然推断类型。这是一个特性,如果类型系统能够正确推断出类型,最好不要手动添加类型注解。类型擦除(ErasedTypes)前面例子中的代码,TypeScript会编译成什么?我们来看看:"usestrict";functiongreet(person,date){console.log("Hello"+person+",今天是"+date.toDateString()+"!");}greet("Maddison",新日期());注意两点:我们的person和date参数不再有类型注解模板字符串,也就是用`包裹的字符串被转换为用+号拼接我们先来看第一点。类型注释不是JavaScript的一部分。所以没有可以直接运行TypeScript代码的浏览器或运行时环境。这就是为什么TypeScript需要一个编译器,它需要将TypeScript代码转换成JavaScript代码,然后才能运行它。所以大多数特定于TypeScript的代码都被清除了,在这种情况下,像我们的类型注释这样的东西都被清除了。记住:类型注解不会改变程序运行时的行为。降级让我们关注第二点。原代码为:`Hello${person},todayis${date.toDateString()}!`;编译为:"Hello"+person+",todayis"+date.toDateString()+"!";你为什么要这样做?这是因为模板字符串是ECMAScript2015(也称为ECMAScript6、ES2015、ES6等)中的一个特性,TypeScript可以将新版本的代码编译成旧版本的代码,例如ECMAScript3或ECMAScript5。将较高版本的ECMAScript语法转换为较低版本的过程称为降级。TypeScript默认转译为ES3,这是一个非常旧的ECMAScript版本。我们也可以使用target选项转换成一些更新的版本,比如执行--targetes2015会转换成ECMAScript2015,也就是说转换后的代码可以在任何支持ECMAScript2015的地方运行。执行tsc--targetes2015hello.ts,我们看看编译成ES2015的代码:新日期());虽然默认目标是ES3版本,但大多数浏览器已经支持ES2015,因此大多数开发人员可以放心地指定ES2015或更新版本,除非您必须兼容有问题的浏览器。严格性不同的TypeScript用户会关注不同的事情。一些用户将寻求更轻松的体验,既可以帮助检查他们程序中的部分代码,也可以享受TypeScript的工具功能。这是TypeScript的默认开发体验。类型是可选的,推断为与大多数类型兼容,并且没有强制检查可能为null/undefined的值。就像tsc在编译出错时仍然会输出文件一样,这些默认选项不会妨碍你的开发。如果您正在迁移JavaScript代码,这就是从一开始就走的路。与之形成鲜明对比的是,也有很多用户希望TypeScript尽可能地检查他们的代码,这也是该语言提供严格模式设置的原因。但不同于拨动开关的形式(要么勾选,要么不勾选),TypeScript提供的形式更像是一个刻度盘,你转动得越多,TypeScript就会越勾选。这需要一些额外的工作,但为了更全面的检查和更准确的工具功能,这是值得的。如果可能,新项目应始终启用这些严格设置。TypeScript有几个用于严格模式设置的开关。除非另有说明,否则文档中的示例均采用严格模式编写。CLI中的strict配置项,或者tsconfig.json中的"strict":true可以同时开启,也可以单独设置。在这些设置中,您最需要了解的是noImplicitAny和strictNullChecks。noImplicitAny在某些时候,TypeScript不会为我们推断类型,此时它会退回到最宽泛的类型:any。这还不是最糟糕的事情,毕竟回退到any和写JavaScript是一样的。但是,过于频繁地使用any会破坏使用TypeScript的目的。您的程序使用的类型越多,您在验证和工具方面获得的帮助就越多,这意味着编写代码时的错误更少。当启用noImplicitAny配置选项时,当类型被隐式推断为any时将抛出错误。strictNullChecks默认情况下,像null和undefined这样的值是可以赋值给其他类型的。这使我们能够以更全面的方式编写一些代码。但是忘记处理null和undefined也会导致很多错误,有些人甚至会称之为百万的错误!strictNullChecks选项可以让我们更明确地处理null和undefined,也可以让我们不用担心忘记处理null和undefined。TypeScript系列TypeScript系列文章由官方文档翻译、重点难点解析、实战技巧三部分组成,内容涵盖入门、进阶、实战。旨在为您提供系统的TS学习教程。整个系列预计约有40篇文章。点此浏览全系列文章,建议顺便收藏本站。微信:“mqyqingfeng”,加我到世优唯一的读者群。如有错误或不准确的地方,请务必指正,万分感谢。如果你喜欢或者有启发,欢迎star,这也是对作者的鼓励。