这是我参与【第四届青训营】笔记创作活动的第 9 天,今天学习了 TypeScript 的一些知识点
1. 什么是 TypeScript
1.1 发展历史
- 2012-10:微软发布了 TypeScript 的第一个版本(0.8)
- 2014-10:Angular 发布了基于 TypeScript 的 2.0 版本
- 2015-04:微软发布了 Visual Studio Code(VSCode)
- 2016-05:@types/react 发布,TypeScript 可开发 React
- 2020-09:Vue 发布 3.0 版本,官方支持 TypeScript
- 2021-11:v4.5 版本发布
1.2 对比 JS
JavaScript
- 动态类型
- 弱类型语言
TypeScript
- 静态类型
- 弱类型语言
解释 静态类型
- 可读性增强:基于语法解析 TSDoc,IDE 增强
- 可维护性增强:在编译阶段暴露大部分错误
在多人合作的大型项目中,获得更好的稳定性和开发效率
JS 的超集
- 包含于兼容所有 JS 特性,支持共存
- 支持渐进式引入与升级
1.3 编辑器推荐
- Visual Studio Code (轻量级,插件系统丰富)
- TypeScript Playground(在线)
2. 基本语法
2.1 基本数据类型
// 字符串
const q = 'string'
// 数字
const w = 1
// 布尔值
const e = true
// null
const r = null
// undefined
const t = undefined
==
// 字符串
const q: string = 'string'
// 数字
const w: number = 1
// 布尔值
const e: boolean = true
// null
const r: null = null
// undefined
const t:undefind = undefined
2.2 对象类型
const typedancer: IBytedancer = {
jobId: 9303245,
name: 'Lin',
sex: 'man',
age: 28,
hobby: 'swimming'
}
interface IBytedancer {
// 只读属性:约束属性不可在对象初始化外赋值
readonly jobId: number,
name: string,
sex: 'man' | 'women' | 'other',
age: number,
// 可选属性:定义在属性可以不存在
hobby?: string,
// 任意属性:约束所有对象属性都必须是该属性的子类型
[key as string]: any
}
2.3 函数类型
function add(x, y) {
return x + y;
}
const mult = (x, y) => x + y;
interface IMult {
(x: number, y: number): number
}
const mult: IMult = (x, y) => x + y;
function add(x: number, y: number): number {
return x + y;
}
const mult = (x: number, y: number): number => x + y;
2.4 函数重载
2.5 数组类型
type IArr1 = number[]
type IArr2 = Array<string | number | Record<string, number>>
type IArr3 = [number, number, string, string]
interface IArr4 {
[key: number]: any
}
const arr1: IArr1 = [1, 2, 3, 4, 5, 6]
const arr2: IArr2 = [1, 2,'3', '4', {a: 1}]
const arr3: IArr3 = [1, 2,'3', '4']
const arr4: IArr4 = ['string', () => null, {}, []]
2.6 补充类型
2.7 泛型
function getRepeatArr(target) {
return new Array(100).fill(target)
}
type IGetRepeatArr = (target: any) => any[]
type IGetRepeatArrR = <T>(target: T) => T[]
// 泛型接口 & 多泛型
interface IX<T, U> {
key: T,
val: U
}
// 泛型类
class IMan<T> {
instance: T
}
// 泛型别名
type ITypeArr<T> = Array<T>
2.8 类型词能够别名 & 类型断言
2.9 字符串 / 数字 字面量
3. 高级类型
3.1 联合 / 交叉类型
为书籍列表编写类型
const bookList = [{
author: 'xiaoming',
type: 'history',
range: '2001-2021'
}, {
author: 'xiaoli',
type: 'story',
theme: 'love'
}]
类型声明繁琐,存在较多重复
interface IHistroyBook {
author: string
type: string
range: striing
}
interface IStoryBook {
author: string
type: string
theme: striing
}
type IBookList = Array<IHistoryBook | IStoryBook>
因此,我们可以使用 联合 / 交叉类型
联合类型:IA | IB; 联合类型表示一个值可以是集中类型之一
交叉类型:IA & IB; 多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
所以,我们可以把上面看的 interface 修改为如下
type IBookList = Array<{
author: string
} & ({
type: 'history',
range: string
} | {
type: 'story',
theme: string
})>
3.2 类型保护与类型守卫
interface IA {
a: 1,
a1: 2
}
interface IB {
b: 1,
b1: 2
}
// 类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域
function getIsIa(arg: IA | IB): arg is IA {
return !!(arg as IA).a
}
function log2(arg: IA | IB) {
if(getIsIA(arg)) {
console.log(arg.a1)
} else {
console.log(arg.b1)
}
}
来看一道试题
实现函数 reverse
实现函数 logBook 类型
函数接收书本类型,并 logger 出相关特征
function logBook(book: IBookItem) {
// 联合类型 + 类型保护 = 自动类型推断
if(book.type === 'history') {
console.log(book.range)
} else {
console.log(book.theme)
}
}
3.3 高级类型
实现 merge 函数类型
要求 sourceObj 必须为 targetObj 的子集
function merge1(sourceObj, targetObj) {
const result = { ... sourceObj }
for(let key in targeObj) {
const itemVal = sourceObj[key]
itemVal && ( result[key] = itemVal )
}
return result
}
function merge2(sourceObj, targetObj) {
return { ...sourceObj, ...targetObj }
}
interface ISourceObj {
x?: string
y?: string
}
interface ITargetObj {
x: string
y: string
}
type IMerge = (sourceObj: ISourceObj, targetObj: ITargetObj) => ITargetObj
类型实现繁琐:若 obj 类型较为复杂, 则声明 source 和 targeet 便需要大量重复 2 遍
容易出错:若 target 增加 / 减少 key,则需要 source 联动去除
所以我们可以实现 TS 的一些内置类型来帮助我们实现
interface IMerge {
<T extends Record<string, any>>(sourceObj: Partial<T>, targetObj: T): T
}
type IPartial<T extends Record<string, any>> = {
[P in keyof T]?: T[P]
}
// 索引类型:关键字 keyof,其相当于取值对象中的所有 key 组成的字符串字面量
type IKeys = keyof {a: string; b: number}; // type IKeys = "a" | "b"
这一部分就是 TS 体操的内容,感兴趣的可以去github搜索
typescript-challenge学习,这里就不多做介绍了
3.4 函数返回值类型
实现函数 delayCall 的类型声明
delayCall 接受一个函数作为入参,其实现延迟 1s 运行函数
其返回 Promise,结果为入参函数的返回结果
function delayCall(func) {
return new Promise(resolve => {
setTimeOut(() => {
const result = func()
resolve(result)
}, 1000)
})
}
假如我们想要知道返回值有哪些你会怎么添加类型限制呢?
type IDelayCall = <T extends () => any>(func: T) => ReturnType<T>
type IReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
简单解释 关键字【
extends】跟随泛型出现的时候,表示类型推断,其表达可类比三元表达式如
T === 判断类型 ? 类型 A : 类型 B==============================================
关键字【
infer】出现在类型推荐中,表示定义类型变量,可以用于指代类型如该情景下,将函数的返回值类型作为变量,使用新泛型 R 表示,使用在类型推荐命中的结果中
4. 工程应用
4.1 Web--Webpack
- 配置 webpack loader 相关配置
- 配置 tsconfig.json 文件
- 运行 webpack 启动 / 打包
- loader 处理 ts 文件时,会进行编译与类型检查
相关 loader
4.2 Node
4.2.1 使用 TSC 编译
- 安装 Node 与 npm
- 配置 tsconfig.json 文件
- 使用 npm 安装 tsc
- 使用 tsc 运行编译得到 js 文件
总结
这篇文章主要介绍了一些 TypeScript 的一些基本语法,其中有很多的知识点,很多人觉得 ts 就是 js 加一层校验,其实不然,使用 ts 开发能大大降低 bug 的出现率以及合理的使用 ts 也能够带来不一样的开发体验~