什么是 TypeScript、基本语法、高级类型、工程应用
# TypeScript 入门
# 什么是 TypeScript
# 发展历史
2012-10:微软发布了 TypeScript 第一个版本 (0.8)
2014-10:Angular 发布了基于 TypeScript 的 2.0 版本
2015-04:微软发布了 Visual Studio Code
2016-05:@types/react 发布,TypeScript 可开发 React
2020-09:Vue 发布了 3.0 版本,官方支持 TypeScript
2021-11:v4.5 版本发布
# 特点
JS:动态类型、弱类型
TS:静态类型、弱类型
JS 的本质是脚本语言,当被执行时才会匹配类型
TS 是不能被直接执行的,会在执行前进行类型匹配,编译后才能执行
# 优势
-
静态类型:
可读性增强:基于语法解析 TSDoc,ide 增强
可维护性增强:在编译阶段暴露大部分错误 => 多人合作的大型项目中,获得更好的稳定性和开发效率
-
JS 的超集:
包含于兼容所有 JS 特性,支持共存
支持渐进式引入与升级
# 基本语法
# 基础数据类型
/* 字符串 */ | |
const q: string = 'string'; | |
/* 数字 */ | |
const w: number = 1; | |
/* 布尔值 */ | |
const e: boolean = true; | |
/* null */ | |
const r: null = null; | |
/* undefined */ | |
const t: undefined = undefined; |
# 对象类型
const bytedancer: IBytedancer = { | |
jobId: 9303245, | |
name: 'Lin', | |
sex: 'man', | |
age: 28, | |
hobby: 'swimming', | |
} | |
interface IBytedancer { | |
/* 只读属性:约束属性不可在对象初始化外赋值 */ | |
readonly jobId: number; | |
name: string; | |
sex: 'man' | 'woman' | 'other'; | |
age: number; | |
/* 可选属性:定义该属性可以不存在 */ | |
hobby?: string; | |
/* 任意属性:约束所有对象属性都必须是该属性的子类型 */ | |
[key: string]: any; | |
} | |
/* 报错:无法分配到 "jobId",因为它是只读属性 */ | |
bytedancer.job1d = 12345; | |
/* 成功:任意属性标注下可以添加任意属性 */ | |
bytedancer.plateform = 'data '; | |
/* 报错:缺少属性 "name", hobby 可缺省 */ | |
const bytedancer2: IBytedancer = { | |
jobId: 89757, | |
sex: 'woman', | |
age: 18, | |
} |
# 函数类型
function add(x: number, y: number): number{ | |
return x + y; | |
} | |
const mult: (x: number, y: number) => number = (x, y) => x * y; |
# 函数重载
/* 对 getDate 函数进行重载,timestamp 为可缺省参数 */ | |
function getDate(type: 'string ' , timestamp?: string): string; | |
function getDate(type: 'date' , timestamp? : string): Date; | |
function getDate(type: 'string' | 'date', timestamp?: string): Date | string { | |
const date = new Date(timestamp); | |
return type === 'string' ? date.toLocaleString() : date; | |
} | |
const x = getDate( 'date' ); // x: Date | |
const y = getDate( 'string', '2018-01-10' ); // y: string |
interface IGetDate { | |
(type: 'string' , timestamp?: string): string; | |
(type: 'date', timestamp? : string): Date; | |
(type: 'string' | 'date ', timestamp?: string): Date | string; | |
} | |
/* 不能将类型 "(type: any,timestamp: any) => string | Date" 分配给类型 "IGetDate"。 | |
不能将类型 "string | Date" 分配给类型 “string”。 | |
不能将类型 "Date" 分配给类型 “string"。ts (2322) */ | |
const getDate2: IGetDate = (type, timestamp) => { | |
const date = new Date(timestamp) | |
return type == 'string' ? date.toLocaleString() : date; | |
} |
# 数组类型
/*「类型+方括号」表示 */ | |
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, {}, []]; |
# 泛型
function getRepeatArr(target) { | |
return new Array(100).fill(target); | |
} | |
type IGetRepeatArr = (target: any) => any[]; | |
/* 不预先指定具体的类型,而在使用的时候再指定类型的一种特性 */ | |
type IGetRepeatArrR = <T>(target: T) => T[]; |
/* 泛型接口 & amp; 多泛型 */ | |
interface IX<T,U>{ | |
key: T; | |
val: U; | |
} | |
/* 泛型类 */ | |
class IMan<T> { | |
instance: T; | |
} | |
/* 泛型别名 */ | |
type ITypeArr<T> = Array<T>; |
/* 泛型约束:限制泛型必须符合字符串 */ | |
type IGetRepeatStringArr = <T extends string>(target: T) => T[]; | |
const getStrArr: IGetRepeatStringArr = target => new Array(100).fill(target); | |
/* 报错:类型 "number” 的参数不能赋给类型"string" 的参数 */ | |
getStrArr(123); | |
/* 泛型参数默认类型 */ | |
type IGetRepeatArr<T = number> = (target: T) => T[]; | |
const getRepeatArr: IGetRepeatArr = target => new Array(100).fill(target); | |
/* 报错:类型 “string"的参数不能赋给类型"number" 的参数 */ | |
getRepeatArr('123'); |
# 类型别名 & 类型断言
/* 通过 type 关键字定义了 I0bjArr 的别名类型 */ | |
type IObjArr = Array<{ | |
key: string; | |
[objKey: string] : any; | |
}> | |
function keyBy<T extends IObjArr>(objArr: Array<T>) { | |
/* 未指定类型时,result 类型为 {(}*/ | |
const result = objArr.reduce(( res, val, key) => { | |
res [key] = val; | |
return res; | |
},{}); | |
/* 通过 as 关键字,断言 result 类型为正确类型 */ | |
return result as Record<string,T>; | |
} |
# 高级类型
# 联合 / 交叉类型
常规写法:繁琐且不准确
interface IHistoryBook { | |
author: string; | |
type: string; | |
range: string} | |
interface IStoryBook { | |
author: string; | |
type: string; | |
theme: string; | |
} | |
type IBookList = Array<IHistoryBook | IStoryBook>; |
联合和交叉类型:
联合类型:IA │ IB;联合类型表示一个值可以是几种类型之一
交叉类型:IA & IB;多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
type IBookList = Array<{ | |
author: string; | |
} & ({ | |
type: 'history'; | |
range: string; | |
} | { | |
type: 'story'; | |
theme: string; | |
})> |
# 类型保护与类型守卫
interface IA { a: 1, a1: 2 } | |
interface IB { b: 1, b1: 2 } | |
function log(arg: IA|IB){ | |
/* 报错:类型 “IA│IB"上不存在属性"a"”。类型 “IB” 上不存在属性 “a" */ | |
/* 结论:访问联合类型时,处于程序安全,仅能访问联合类型中的交集部分 */ | |
if (arg.a) { | |
console.log(arg.a1) | |
} else { | |
console.log(arg.b1); | |
} | |
} | |
/* 类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域 */ | |
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); | |
} | |
} |
# 高级类型
interface IMerge { | |
<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"“ | |
// 关键字【in】,其相当于取值字符串字面量中的一种可能,配合泛型 P,即表不母个 key | |
// 关键字【?】,通过设定对象可选选项,即可自动推导出子集类型 |
# 函数返回值类型
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 表示,使用在类型推荐命中的结果中 |
# 工程应用
-
Webpack
在 webpack 中导入 awesome-typescript-loader、babel-loader,配置 tsconfig.js 文件
-
Node.js
使用 npm 安装 tsc,配置 tsconfig.js,运行 tsc 编译 ts 文件