TypeScript中你可能会忽略的细节

作者&投稿:晨盛 (若有异议请与网页底部的电邮联系)
~

本文默认你已经在项目中实践了TypeScript(以下简称ts),本文不会讲解什么是ts,ts的具体语法等。本文只是对在使用ts开发项目中开发者可能会忽略的一些细节做一些总结。

回顾下TypeScript简单用法leta:string='hellots'//显示注解声明letb='hellots'//ts自动推导typeA=string//类型别名interfacePerson{//接口name:stringage:numbercount:numberfriends:{name:stringage:numberaddress:string}[]}interfaceShinjiextendsPerson{Auto:string}enumWeekday{//枚举Mon,Tue,Wed,....}lettuple:[number,string]=[1,'2']//元组interfaceNerv{attack(target:string):void}classEVAimplementsNerv{//类与接口实现implementsprivatereadonlyRobotType:string='Zero'attact(target:string){console.log(target)}}

那些你可能忽视的细节const声明的类型推导

ts作为一种使用了两种类型系统实现的语言,既可以通过注解显示声明类型,也可以通过ts自动推导类型。请看下面例子,参数a的值通过ts类型推导,推导为boolean类型,那么参数bts是否会自动推导出来是boolean类型呢?

leta=true//booleanconstb=true//=>?

看一下结果:

很明显,参数b被自动推导为了true而不是boolean,原因是因为,这里使用的是const而不是let或者var。使用const声明的基本类型的值,赋值后无法修改,因此ts推导出的范围是最窄范围,也就是c的类型是true而不是boolean

那么我们再看一下下面这段const声明对象时的类型推导

consta={b:12}

这里很奇怪的是ts推导出来的b居然是一个number类型,而不是字面量12。上面第一个例子来说const或者let声明是对ts推导有影响的,这些都是作用于基本类型,使用const声明对象的话,ts的推导就不会缩窄,这条规则也就失效了。这种情况类似esnext中的const声明。

索引签名

索引签名的句法为[key:T]:U,我们通过索引签名告诉ts,指定的对象可能有更多的键。这种句法的意思是,“在这个对象中,类型为T的键对应的值的类型为U”。

需要注意的点:T的类型必须可以赋值给number|string。当然key的值可以为任何词,不局限于非得用'key'

元组

元组是array类型的子类型,是定义数组的特殊方式,长度固定,各索引上的值,具有固定的类型。

leta:[number]=[1]letb:[number,string]=[1,'2']

元组可以支持可选属性

letc:[number,string?]=[1]

元组可以支持剩余参数即...rest的形式

letd:[string,...string[]]=['shinji','rei','asuka']lete:[number,boolean,...string[]]=[3,true,'hello','ts']

上面是元组的基本用法,但是ts实际在推导元组的时候会放宽要求,ts推导出来的类型尽可能的宽泛,而不是收窄,实际上做出来的推导并不会在乎元组的长度,以及元组所在索引值的类型,也就是放宽到元组会默认推导为数组,毕竟元组是array的子类型。看下面的代码示例:

leta=[1,true]//没有显示注解声明,猜一下ts会自动推导出来什么?

揭晓答案:

浓眉大眼的居然推导出来是T[]的类型。

如果我们想上面的代码就是要推导出为元组类型,有没有解决办法?有,当然有,一共有三种方式。

方式一:

leta=[1,true]as[number,boolean]

方式二:

这种asconst的方式有副作用,会把类型置为readonly并且类型变为const类型,即a的实际类型为:readonly[1,true]

leta=[1,true]asconst

方式三:

我们可以使用ts推导剩余参数的类型方式,将元组的类型收窄

functiontuple<Textendsunknown[]>(...args:T):T{returnargs}leta=tuple(1,true)

这里的关键在于...args:T,由于范型T描述的是剩余参数,因此ts推导出来是一个元组类型。

当项目中使用了大量的元组类型,但又不想使用ts默认的类型推导,我们就可以利用这个技术。

枚举

枚举的作用是列举类型中包含的各个值,枚举是一种无序的数据结构,把键映射到值上。

1个枚举可以分成多次声明,ts会自动将其合并,和interface自动合并一个效果。

leta=true//booleanconstb=true//=>?0

ts较为灵活,允许通过值访问枚举,也可以通过键访问枚举,但是容易出现两种问题

leta=true//booleanconstb=true//=>?1

我们来看下上面第二行代码的错误

我们再来看一下Language[6],WTF,langD居然被推导为string,显然有问题啊。

为了解决以上不安全的访问操作,我们可以通过constenum指定使用枚举的安全子集

leta=true//booleanconstb=true//=>?2

使用安全子集的话该报错的就都会提示了,原因是:constenum不允许反向查找,此行为与普通的JavaScript对象很像。而且使用了constenum后,ts是不会生成js代码的,此功能谨慎使用。如果想在使用constenum的同时又生成js代码,请在ts.config.js中开启preserveConstEnums选项。

TS中this类型注解

this在js中是一个很特殊的存在,这里不过多的讨论this的用法,来聊一下this除了可以作为值

之外,还可以作为类型。

由于this调用方式的特殊性,在ts中我们有“保底”方案,对于函数来说,如果你定义的函数中使用了this,可以在函数的第一个参数中声明this的类型,这样每次调用函数的时候,ts确保this的类型一定是你预期声明的类型,看代码:

leta=true//booleanconstb=true//=>?3

对于类来说,this的类型也可以用于注解的方法返回类型。

我们实现一个ES6中的简易Set数据结构,首先看下ES6Set的用法

leta=true//booleanconstb=true//=>?4

我们同样用ts实现一个简易Set(不做具体实现,只做类型说明)

leta=true//booleanconstb=true//=>?5

这样做是完全可以实现的,但是我们想要在定义SimpleSet的子类呢?

leta=true//booleanconstb=true//=>?6

由上面代码可以看到,每当我们扩展一个派生类的时候,都要把add方法返回的this签名给覆盖掉,比较麻烦。有没有更好的办法不这么麻烦?有。

我们可以使用this类型注解重写以下SimpleSet类:

leta=true//booleanconstb=true//=>?7

范型T

范型是一种多态类型参数,通常的使用方式是尖括号<T>,当然你也可以用其他字母等。

范型参数使用<>尖括号来声明,<>尖括号的主要作用就是<>所处的位置限定了范型的作用域,ts会确保当前作用域中相同的范型会最终绑定为同一个具体的类型。看下代码

leta=true//booleanconstb=true//=>?8

那么范型是在什么时候被绑定具体的类型呢?声明范型的位置不仅限定范型的作用域,还决定了ts什么时候为范型确定绑定的具体类型

leta=true//booleanconstb=true//=>?9

一句话总结就是,ts在使用范型时为范型绑定具体类型:对函数来说,在函数调用时绑定,对类来说,在类实例化时绑定,对类型别名和接口来说,在使用别名和实现接口时。

范型声明的位置?

看代码:

consta={b:12}0

注意,在构造方法中不能声明范型。应该在类声明中声明范型belikeclassFilter<T,U>

快速区分void、any、unknown、never

简单的通过几个代码片段快速区分一下这几个类型,这里不再过多赘述。

consta={b:12}1

快速了解型变

先看一小段代码:这里看TextendsU,我们可以模拟的认为T<:U,换句话来说,就是T类型是U类型的子类型,或者为同种类型。这样就一定能保证T类型可以赋值给U类型。

consta={b:12}2

上面的代码就是ts中型变中的协变,那么我们使用T<:U这种形式快速来了解下ts型变中的其他方式:

consta={b:12}3

在ts中每个复杂类型的成员都会协变,包括对象、类、数组和函数。

函数的协变与其他类型的协变是稍有不同的,这里不多讨论。

函数类型重载

先来明确一下什么是重载函数,重载函数就是有多个调用签名的函数。先看下怎么写函数调用签名:

consta={b:12}4

两种写法等效,只是使用方式不同而已。但是在函数类型重载的情况下,更推荐完整型的写法。

我们先看下一函数类型重载的例子:

consta={b:12}5

运行上面这段代码我们会发现错误,错误如下:

错误造成的原因来说一下,这是ts的调用签名重载机制造成的。如果为函数reserve声明多个重载的签名,在调用方看来,reserve方法的类型是各个调用签名的并集。所以在实现reserve函数的时候,我们需要自己去声明组合后的调用签名,这是ts无法自动推导的。我们可以将reserve函数改为下面的方式就可以解决:

consta={b:12}6

也就是说在手动实现函数的时候,要实现这两个调用签名的并集。

关于调用签名重载,可以查看浏览器DOMAPI,DOMAPI中有大量重载。

伴生对象模式

伴生对象模式应该是一个不太能常见到的概念,伴生对象模式来自Scala,目的是为了把同名的对象和类配对在一起。

了解伴生对象模式之前我们需要知道,在ts中的类型和值分别在不同的命名空间中的。这就意味着,在同一个作用域中可以存在同名的类型和值。比如在类中,类可以声明值也可以声明类型:

consta={b:12}7

了解完上面的概念后,我们看下伴生对象模式:

consta={b:12}8

使用以上伴生对象模式有几个好处:

可以语义上归属统一名称的类型和值

使用方可以一次性导入两者

除了可以将伴生对象模式用到值和类型别名外,接口和命名空间也可以使用伴生对象模式。

安全的扩展原型

由于JS是一门十分动态的语言,所以我们可以在JS运行时任意修改内置的方法,比如数组的push、Object.assign()等。所以对于JS来说,动态扩展远行是一种不安全的行为。但是有了ts,我们可以放心的扩展原型。

举个?:我们想给Array的原型添加一个zip方法,为了能够安全的扩展Array原型,我们需要做两件事:

比如在zip.ts文件中扩展Array的原型

新增zip方法,增强原型功能。

代码:

consta={b:12}9

非空断言

我们在使用ts的时候经常会使用类型断言aasstring的形式来明确告诉ts这个就是我们预期的类型。那么什么是非空断言?

我们先看下那些类型可以为空,T|null或T|null|undefined,这是比较特殊的类型,在ts中专门为此提供语法,用于断定类型为T而不是null或者undefined。

我们来看下代码:

leta:[number]=[1]letb:[number,string]=[1,'2']0

上面代码其实会有两处错误,我截图标注出来

我们先看下第一个错误,第一个错误,由于document.getElementById(dialog.id)处于一个新的作用域中,ts并不知道代码会不会修改dialog,所以此时ts的代码收窄不起作用,虽然dialog.id存在绝对可以确定DOM中有该id对应的元素,但是ts看来,调用document.getElementById(dialog.id)返回的类型会是HTMLElement|null。

第二个错误就是,虽然我们一定知道element一定有父节点,但是ts依旧会推断element.parentNode的类型是Node|null

解决方法当然有:

最暴力的就是any一把梭,我们可以类型断言为any即anythingasany

可以使用大量的if(_===null)来进行判断,确保我们ts不会报错。

使用非空断言,代码如下:

leta:[number]=[1]letb:[number,string]=[1,'2']1

使用非空断言的目的是为了明确的告诉ts我们确定dialog.id、document.getElementById函数调用和element.parentNode得到的结果是已经定义好的。这样ts就不会报错了。

与非空断言相反的应该就是“明确赋值断言”,这里不过多赘述。

模拟名义类型(隐含类型)

首先,我们需要知道的是ts是结构化的类型系统。但是我们可以通过ts来实现名义类型。

什么是名义类型?

首先让我们段代码了解一下什么是结构化类型系统

leta:[number]=[1]letb:[number,string]=[1,'2']2

这时候名义类型就派出用场了!让我们通过代码来看一下怎么使用名义类型,名义类型在ts使用并不顺滑,但是在大型的项目或者大型的团队来说,名义类型能够更好的避免错误。

leta:[number]=[1]letb:[number,string]=[1,'2']3

报错信息:

虽然使用string&{readonlybrand:uniquesymbol}看起来很乱,但是没有其他更好的办法,这里使用了uniquesymbol来作为唯一的flag的原因是因为在ts中实际上有两个真正意义上的名义类型,一个是uniquesymbol另一个就是enum。

辨别并集类型

我们先看一段代码:

leta:[number]=[1]letb:[number,string]=[1,'2']4

为什么event.value可以细化,但是event.target不能细化?因为handle函数的类型是UserEvent,但并不意味着一定传入UserTextEvent或者UserMouseEvent类型的值,甚至还可以传入两者的并集。由于并集类型的成员又可能重复,所以ts用了一种更稳妥的方式明确了并集类型的具体情况。

如何解决这种ts无法细化并集的问题呢?看代码:

leta:[number]=[1]letb:[number,string]=[1,'2']5

我们只需要一个字面量来标记并集类型的各种情况即可,但是这个字面量要满足以下几个:

在并集各个组成部分位置相同。

使用字面量类型(string,number,boolean等)。

不能使用范型。

要互斥,即在并集中是独一无二的。

非常好用的映射类型

ts提供了非常强大好用的映射类型,比如内置的Record的实现。

leta:[number]=[1]letb:[number,string]=[1,'2']6

既然提供了这么好用的映射类型,那我们看下映射类型能做什么?

leta:[number]=[1]letb:[number,string]=[1,'2']7

说明一下:

?运算符可以将类型标记为可选的。

readonly可以把类型标记为只读的。

-运算符可以撤销?和readonly,-运算符需要置于?和readonly之前。

键入运算符

键入运算符比较简单,和对象取值的操作类似,看一下代码就会明白:

leta:[number]=[1]letb:[number,string]=[1,'2']8

inferR

inferR属于条件类型的一种,是可以在条件中声明的范型。回顾一下我们以前使用范型是怎么用的?

typeElementType<T>=Textendsunknown[]?T[number]:T

。但是在条件类型中的声明,我们并不采用这种<T>尖括号的方式,我们使用infer关键字。

leta:[number]=[1]letb:[number,string]=[1,'2']9

我们来看个稍微复杂的例子:

letc:[number,string?]=[1]0

从某种意义上来说inferR等同于范型声明,即inferR==T,inferR属于行内声明。

原文:https://juejin.cn/post/7111351347114410020


typescript和javascript的区别(typescript与javascript区别)_百 ...
TypeScript入门指南 新系列深入浅出TypeScript来了,本系列至少20+篇。本文为第一篇,来介绍一下TypeScript以及常见的类型。 TypeScript是一门由微软推出的开源的、跨平台的编程语言。它是JavaScript的超集,扩展了JavaScript的语法,最终会被编译为JavaScript代码。 TypeScript的主要特性: TypeScript主要是为了实现以下两个目...

typescript和js的区别
TypeScript和JavaScript是目前项目开发中较为流行的两种脚本语言,typescript和js的区别:1、TypeScript可以使用JavaScript中的所有代码和编码概念,TypeScript是为了使JavaScript的开发变得更加容易而创建的。例如,TypeScript使用类型和接口等概念来描述正在使用的数据,这使开发人员能够快速检测错误并调试应用程序。

ts是什么格式的文件?
TS文件是一种TypeScript源代码文件。详细解释如下:TS文件是TypeScript语言编写的源代码文件。TypeScript是一种由微软开发的开源编程语言,它是JavaScript的一个超集,意味着它添加了静态类型系统和一些额外的功能来增强JavaScript的功能。TypeScript的特点:1. 静态类型系统:TypeScript提供了静态类型系统,允许...

ts文件是什么
ts文件是TypeScript编译后的文件。以下是对TypeScript及其编译后文件的详细解释:1. TypeScript简介:TypeScript是一种由微软开发的开源编程语言。它是一种JavaScript的超集,增加了静态类型系统和许多高级功能,如类和接口等。这使得TypeScript代码更加易于维护和理解,特别是在大型项目中。开发者可以在Type...

ts附体是什么意思?
TS是TypeScript的简称,它是JavaScript的超集,集成了静态类型检查、类、接口等功能。TS附体就是程序员使用TS语言时,像进入了一种状态,会把逻辑思维切换到这种语言的模式下,更加严格地检查代码的类型和逻辑。这种状态的转换需要一定的学习和适应时间,但可以提高编写代码的质量和效率。TS附体让程序员更加...

tsc是什么意思
TSC是TypeScript编译器的缩写。TypeScript是一种由微软开发的开源编程语言。作为JavaScript的一个超集,TypeScript为开发者提供了静态类型系统和其他一些强大的功能,以提高代码的可读性和可维护性。而TSC作为TypeScript的编译器,其作用是将TypeScript代码转化为可以在浏览器或服务器上运行的纯JavaScript代码。...

TypeScript条件类型精读与实践
在大多数程序中,我们必须根据输入做出决策。TypeScript也不例外,使用条件类型可以描述输入类型与输出类型之间的关系。用于条件判断时的extends当extends用于表示条件判断时,可以总结出以下规律 若位于extends两侧的类型相同,则extends在语义上可理解为===,可以参考如下例子:typeresult1='a'extends'abc'?

typescript接口?
typescript接口请求的类型文件放哪放到car.ts里面。另外:推荐首字母大写。typeCategory='roadster'|'suv'比如我们在cat.ts里面还有一个函数,参数用到了这个类型。举例:functionfn(cat:Category):string;当我们在.vue里面用时,是不需要显式定义参数类型的:letcat='suv'我们不需要写:letcat:Category=...

ts是什么意思
TS是指TypeScript。TypeScript解释 1. 基本定义 TypeScript是一种由微软开发的开源编程语言。它实际上是JavaScript的一个超集,意味着TypeScript代码可以无障碍地在JavaScript环境中运行,同时它添加了静态类型系统和其他功能,以帮助开发者编写更大规模、更可靠的应用程序。2. 静态类型系统 TypeScript的静态...

ts代表什么
TS代表TypeScript。解释:1. 基本定义:TS是TypeScript的简称。TypeScript是一种由微软开发的开源编程语言。2. 类型系统:TypeScript建立在JavaScript的基础上,并引入了静态类型系统。这意味着在开发过程中,开发者可以为变量、函数等定义类型,从而增强代码的可读性和可维护性。这种类型系统可以帮助开发者在...

宜君县19423627637: TypeScript 和 JavaScript 的区别 -
张喻单硝: TypeScript 和 JavaScript 是目前项目开发中较为流行的两种脚本语言,我们已经熟知 TypeScript 是 JavaScript 的一个超集.JavaScript 和 TypeScript 的主要差异: 1、TypeScript 可以使用 JavaScript 中的所有代码和编码概念,TypeScript 是为了...

宜君县19423627637: CAD中复制粘贴时出现:pasteclip 忽略块 - Oblique 的重复定义. 无法粘贴 -
张喻单硝: CAD中复制粘贴时出现:pasteclip忽略块Oblique 的重复定义的原因可能有以下几点,具体操作如下: 1、首先打开CAD软件如下图所示. 2、CAD软件本身问题:在CAD中通过co或者cp进行复制或者通过在其他软件中复制内容到CAD中粘...

宜君县19423627637: TypeScript 中的 d.ts 文件有什么作用,这种文件的内如如何编写 -
张喻单硝: TypeScript 相比 JavaScript 增加了类型声明.这些类型声明帮助编译器识别类型,从而防止开发者“搬起石头砸自己的脚”.原则上,TypeScript 需要开发者做到先声明后使用.这就导致开发者在调用很多原生接口(浏览器、Node.js)或者第三方模块的时候,因为某些全局变量或者对象的方法并没有声明过,导致编译器的类型检查失败.

宜君县19423627637: typescript中的map方法是什么意思 -
张喻单硝: 也可以通过在 tsc 中添加 --sourcemap 参数来创建映射文件,浏览器会对其自动加载、自动解析,实现可以调试TypeScript的功能.

宜君县19423627637: typescript 装饰器是什么东西 -
张喻单硝: 随着TypeScript和ES6里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员. 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式. Javascript里的装饰器目前处在 建议征集的第一阶段,但在TypeScript里已做为一项实验性特性予以支持.装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上. 装饰器使用@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入.

宜君县19423627637: XML 注释必须是一行中的第一条语句. XML 注释将被忽略. -
张喻单硝: 1:注释的语句肯定是会被忽略的.2:不一定是一行中的第一条语句,XML文档内的任何部分都可以注释.3:如果你需要用的注释的部分,那么把注释的标示符去掉就行了.如果注释的代码你根本用不到,那就别去管它,系统只是提示你有注释的部分将被忽略,对你的程序运作无影响.如果你觉得提示看着不习惯,直接把注释的地方全部删除,就可以了.,去掉这个符号,那么注释的代码就可以使用了.

宜君县19423627637: 如何在TypeScript中应用像Jquery之类的第三方JavaScript框架 -
张喻单硝: 要在TypeScript引用第三方JavaScript库和框架,首先要了解TypeScript的类型定义文件.TypeScript的类型定义文件用来帮助开发者在TypeScript中使用已有的 javascript的工具包,如:jQuery.所有的类型定义文件都是以.d.ts结尾的.这个文件...

宜君县19423627637: 如何编译TypeScript -
张喻单硝: 1、手动编译1.1、首先找到TypeScript的安装目录,我的在”C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.0“.1.2、使用cmd工具命令cd到安装目录.1.3、输入命令:tsc 文件名.ts,回车编译.一旦编译成功,就会在相同目录下生成同名的js文件(编译成功后是没有任何消息提示的.上图中,这也是编译成功的.只要不存在语法错误).2、设置自动编译

宜君县19423627637: Adobe pr cc安装时语音选择中怎么没有简体中文?是不是Adobe官方不再推出简体中文版? -
张喻单硝: 应该是软件源程序有问题,所以才会造成这种情况的没有自带语言包. 1.启动安装程序的时候,会出现这样的提示框点击“忽略”就可以了. 2.之后的会有安装和试用两种,点击“试用”进入安装. 3.接着会要求用户进行登录,没有账号的用户...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网