【你应该掌握的】深入浅出typescript

TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。
TypeScript可以解决JavaScript弱类型和没有命名空间,难以模块化的问题,同时也增强了代码的可读性,在团队协作和大型项目中体现出更大的优势。
本文将从“基础用法、高阶用法、模块、react项目实践中的应用”四个方向展开文章,方便大家理解,都会备注列子、codesandbox上的远程代码。
基础用法
1.变量类型
代码示例 src/components/baseVar.tsx

TypeScript提供与JavaScript几乎相同的数据类型(布尔值boolean,字符串string,数字number,数组[],元组),此外还提供了枚举enum类型。
当不确定变量的数据类型或者数据类型会在代码运行过程中变化时,使用any来标记这些变量,使它们通过编译阶段的检查。
object表示原始类型以外的类型,即number,string,boolean,symbol,null,undefined以外的类型。
void用于表示没有任何类型,通常用于方法没有返回值时。void类型的变量只能赋值undefined或者null。
null和undefined各自的类型分别为null和undefined,并没有太大用处。
never表示永不存在的类型或方法无法达到终点,当代码中抛出异常或者while(true)时可能会用到。
symbol(自ECMAScript 2015起的新的原生类型),不可改变且唯一,通过Symbol函数构造。TypeScript中内置了一些symbols表示语言内部行为。
类型断言: as / <> / !
当TypeScript无法推断出对象类型但开发者可以确定时,可以通过类型断言来显式表达变量类型。

在实际使用过程中,原始类型和any以及类型断言较为常用,object,void,null,undefined,never,symbol不经常使用。

2.接口
代码示例 src/components/interface.tsx
相较于基础类型而言,接口用于更具体地声明更加复杂的对象结构,关键字为interface。
声明方法

方法体内的入参名称不要求与接口声明中完全一致。
声明变量

变量声明中:

readonly关键字表示属性只读,不可修改。
?表示可选属性,即可以不传该字段,可选参数在必传参数后面。
[propName: string]:any可以匹配到除前面已声明属性外的所有字符串名称的属性,如:sex: boolean, address: any, email: string等,可用于不确定入参中是否还包含其他属性时。

方法的属性排序不要求与接口声明完全一致。
设置默认值

默认值属于可选参数的一种,但默认值不要求在必选参数之后。
当默认值在可选参数前时,需要传入undefined来获取默认值。
剩余参数
当方法中传参数量不一定或需要批量操作入参时,可以通过剩余参数来操作多个参数。

3.简单的高级类型
代码示例 src/components/types.tsx
交叉类型(&)
交叉类型是将多个类型合并为一个类型,包含所有所需类型的所有属性。

联合类型(|)
联合类型表示一个值可以是几个类型之一。

如果一个值是联合类型,我们只能访问所有类型的共有成员。

字符串/数字字面量类型
字符串字面量类型允许指定字符串类型的固定值。
数字字面量类型允许指定数字类型的固定值。

4.迭代器
代码示例 src/components/for.tsx
因为一些内置的类型Map,Array等实现了各自的Symbol.iterator,因此他们都是可迭代的。
(Symbol.iterator方法,被for-of语句调用。返回对象的默认迭代器)
以下语句可用于遍历可迭代对象。
for…of…
(当生成目标为ES5或ES3,迭代器只允许在Array类型上使用。 在非数组值上使用 for…of语句会得到一个错误,就算这些非数组值已经实现了Symbol.iterator属性。)

for…in…

高阶用法
1.泛型
代码示例 src/components/identity.tsx
泛型,指不预先确定的数据类型,在使用时才去确定。这里的使用时,不是指代码运行时。
泛型的本质是将类型参数化,这种参数化的类型可以使用在类、接口及方法中成为泛型类、泛型接口、泛型方法。
泛型是用于创建可复用代码组件的重要工具,使得代码片段可以被多种数据类型使用。
考虑当一个方法有多种可能类型的入参,或者一个变量有多种可能类型的赋值时,通过联合类型列出所有可能的类型有时候是不现实或者繁杂的。当然,我们也可以将其定义为any类型来通过编译阶段的检查,但带来的问题是类型准确性的丢失,调试时也无法看到完整的参数信息。正是因此,TypeScript并不推荐使用any,因为使用any与不引入TypeScript是没有太大区别的。
泛型可用于解决这类问题。
书写泛型接口或方法或类时,我们通过一个类型变量来捕获参数的类型,显示在**尖括号<>**中,命名随意,通常以大写字母代表。
在实际调用该方法时,给类型变量赋予实际的类型,如string,array,number,boolean等;如不赋值具体类型,则TypeScript通过类型推断来检查代码。

在示例代码中,func1,func2,func3返回与入参类型一致的出参。< T > < U >捕获入参的类型,并用于规范方法的出参。
泛型约束
有时,我们对方法的入参虽然不限制类型,但仍然期望他具备某个属性或者满足某些限制,此时我们可以使用extends关键字继承接口来给泛型增加约束。

示例中func4与func5的区别在于,func5中T extends Leng。当给T增加了约束必须具备length属性时,arg就被限制了类型不可以是number,并且arg.length也不存在undefined的情况。
2.复杂的高级类型
区分类型
代码示例 src/components/types.tsx
当出现**联合类型(Person | Worker)**时,有时我们需要调用非共有属性,此时需要进一步确认当前变量究竟属于哪个类型。TypeScript提供了一些方法进行类型区分。

typeof / instanceof
typeof与instanceof可以被TypeScript识别为原始类型的类型保护,我们可以借此进行原始类型的区分。

用户自定义的类型保护
当需要对非原始类型进行判断时,用户可以自定义一个类型保护函数,返回值是一个类型谓词,类型谓词形式为paramName is TypeName。

该示例中isWorker就是一个类型保护函数,用于判断入参是否是Worker类型,类型谓词是p is Worker。

类型断言
如果开发人员确定当前变量在上下文是什么类型,也可以通过类型断言as/<>显式表明当前变量类型来通过TypeScript的检查。

在实际开发需求中,我们也经常会遇到某些参数或变量可能为null/undefined的情况,此时调用某些方法就会产生报错。
我们可以使用类型断言标记!后缀手动去除null和undefined。

类型别名(Type)
代码示例 src/components/types.tsx
类型别名会给类型(包括原始类型)起一个新的名字。给原始类型取别名可以增强代码可读性,类似于文档,但较少使用。类型别名更多地使用于非原始类型的情况。
类型别名(type)与接口(interface)非常相似,但存在以下几点区别:

接口创建了新的名字,但类型实际上没有
接口可以被extends和implements,但类型不支持
如果需要使用联合类型或交叉类型,这时会选择类型别名

可辨识联合
代码示例 src/components/union.tsx
可辨识联合也称标签联合或代数数据类型。它是将单例类型,联合类型,类型保护和类型别名合并起来创建的,具有以下三个特征:

具有普通的单例类型属性 - 是该数据类型可辨识的特征
一个数据类型包含了多种类型的联合
该属性上的类型保护 (可以通过单例类型属性进行类型区分)

发布了68 篇原创文章 · 获赞 1 · 访问量 938

猜你喜欢

转载自blog.csdn.net/A669MM/article/details/104953969