什么是 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 特性,支持共存 支持渐进式引入与升级
# 基本语法
# 基础数据类型
代码语言:javascript复制/* 字符串 */
const q: string = 'string';
/* 数字 */
const w: number = 1;
/* 布尔值 */
const e: boolean = true;
/* null */
const r: null = null;
/* undefined */
const t: undefined = undefined;
# 对象类型
代码语言:javascript复制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,
}
# 函数类型
代码语言:javascript复制function add(x: number, y: number): number{
return x y;
}
const mult: (x: number, y: number) => number = (x, y) => x * y;
# 函数重载
代码语言:javascript复制/* 对 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
代码语言:javascript复制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;
}
# 数组类型
代码语言:javascript复制/*「类型+方括号」表示 */
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, {}, []];
# 泛型
代码语言:javascript复制function getRepeatArr(target) {
return new Array(100).fill(target);
}
type IGetRepeatArr = (target: any) => any[];
/* 不预先指定具体的类型,而在使用的时候再指定类型的一种特性 */
type IGetRepeatArrR = <T>(target: T) => T[];
代码语言:javascript复制/* 泛型接口 & amp; 多泛型 */
interface IX<T,U>{
key: T;
val: U;
}
/* 泛型类 */
class IMan<T> {
instance: T;
}
/* 泛型别名 */
type ITypeArr<T> = Array<T>;
代码语言:javascript复制/* 泛型约束:限制泛型必须符合字符串 */
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');
# 类型别名 & 类型断言
代码语言:javascript复制/* 通过 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>;
}
# 高级类型
# 联合 / 交叉类型
常规写法:繁琐且不准确
代码语言:javascript复制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;多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
代码语言:javascript复制type IBookList = Array<{
author: string;
} & ({
type: 'history';
range: string;
} | {
type: 'story';
theme: string;
})>
# 类型保护与类型守卫
代码语言:javascript复制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);
}
}
# 高级类型
代码语言:javascript复制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
// 关键字【?】,通过设定对象可选选项,即可自动推导出子集类型
# 函数返回值类型
代码语言:javascript复制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 文件