「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」
一、前言
没写ts之前看官方文档,被那么多
特性吓尿了,实际上写业务用的特性不多。五月底ts正式升级了4.3版本,4.4也已经在beta版本了。差不多先生,够用就行,这里更多的是,以自己的理解去总结一下常用的ts特性。有理解错误的,欢迎大哥指正
👏🏻
知识这种东西,学了还是要总结下,梳理清楚自己对当前知识的掌握程度。偷懒了一段时间,接下来要好好发力了。
首发博客:俊劫的学习基地 欢迎star,一起学习!博客主页有吹水群,扫码加入!
二、ts的优缺点
1、优点
代码的可读性和可维护性
:举个🌰看后端某个接口返回值
,一般需要去network看or去看接口文档,才知道返回数据结构,而正确用了ts后,编辑器会提醒
接口返回值的类型,这点相当实用。- 在
编译阶段
就发现大部分错误,避免了很多线上bug
- 增强了编辑器和 IDE 的功能,包括
代码补全
、接口提示
、跳转到定义
、重构
等
2、缺点
- 有一定的
学习成本
,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念 - 会增加一些
开发成本
,当然这是前期的,后期维护更简单了 - 一些JavaScript库需要
兼容
,提供声明文件,像vue2,底层对ts的兼容就不是很好。 - ts编译是需要
时间
的,这就意味着项目大了以后,开发环境启动和生产环境打包的速度就成了考验 - 可以看看Deno 内部代码将停用 TypeScript,并公布五项具体理由
或多或少,听到过的开发体验最好的架构:React Hooks + TypeScript
。目前也在用,还在学习中,至于到底好不好,我还是对vue
情有独钟。前端还在快速发展中,后面再出来个xxxScript
,谁也说不好。所以一个字:学!
三、anyScript
可能因为业务场景或者业务紧张,or某个跑路的大哥省了点功夫,用了typeScript的项目也可能会变成anyScript
。以下是几种救急的方式(大哥们还没有其他办法
):
- // @ts-nocheck 禁用整个文件的ts校验
- // @ts-ignore 禁用单行ts校验
- any和unknown 不建议多用,但也不是不能用,有些场景确实不好写ts定义。这个时候就不要硬憋自己了,写个备注any下。
抛个面试题:
你知道any和unknown的区别吗?
回归正题,开始学习,总结一些项目中使用较多的,一些TS高级特性这里就不说了。
四、ts类型
本篇所有demo都可在TypeScript Playground 运行,不理解的建议都来跑跑看。
1、基础类型
- 常用:boolean、number、string、array、enum、any、void
- 不常用:tuple、null、undefined、never
const count: number = 20210701;
2、对象类型
简单理解interface 和 type 的区别:type 更强大,interface 可以进行声明合并
,type 不行;
看个人习惯,一般声明都用interface,需要用到其他变量类型,type多一些。有没有interface或type一把梭的
🤣?
interface Hero {
name: string;
age: number;
skill: string;
skinNum?: number;
say(): string; // say函数返回值为string
[propname: string]: any; // 当前Hero可定义任意字符串类型的key
}
// 继承
interface littleSoldier extends Hero {
rush(): string;
}
// 任意类型
interface IAnyObject {
[key: string]: any;
}
type Hero = {
name: string,
age: number,
skill: string,
skinNum?: number,
};
3、数组类型
项目中常见的写法,需要声明列表数据
类型:
interface IItem {
id: number;
name: string;
isGod: boolean;
}
const objectArr: IItem[] = [{ id: 1, name: '俊劫', isGod: true }];
// or
const objectArr: Array<IItem> = [{ id: 1, name: '俊劫', isGod: true }];
const numberArr: number[] = [1, 2, 3];
const arr: (number | string)[] = [1, "string", 2];
4、元组 tuple
元组和数组类似,但是类型注解时会不一样
赋值的类型、位置、个数需要和定义(生明)的类型、位置、个数一致。
暂时没用过,感觉用处不大~~~
// 数组 某个位置的值可以是注解中的任何一个
const LOL: (string | number)[] = ["zed", 25, "darts"];
// 元祖 每一项数据类型必须一致
const LOL: [string, string, number] = ["zed", "darts", 25];
5、联合| or 交叉&类型
- 联合类型:某个变量可能是多个 interface 中的其中一个,用
|
分割 - 交叉类型:由多个类型组成,用
&
连接
// anjiao 某胖博主爱好
interface Waiter {
anjiao: boolean;
say: () => {};
}
interface Teacher {
anjiao: boolean;
skill: () => {};
}
// 联合类型
function judgeWho(animal: Waiter | Teacher) {}
// 交叉类型
// 同名类型会进行合并,同名基础类型属性的合并返回:never
// 同名非基础类型属性可以正常合并
function judgeWho(jishi: Waiter & Teacher) {}
6、enum枚举
提高代码可维护性,统一维护某些枚举值,避免 JiShi === 1
这种魔法数字。JiShi === JiShiEnum.BLUEJ
这样写,老板一眼就知道我想找谁。
// 初始值默认为 0
enum JiShiEnum {
REDJ,
BLUEJ,
GREENJ,
}
// 设置初始值
enum JiShiEnum {
REDJ = 8,
BLUEJ,
GREENJ,
}
const jishi: JiShiEnum = JiShiENUM.BLUE
console.log(jishi) // 9
// 字符串枚举,每个都需要声明
enum JiShiEnum {
REDJ = "8号",
BLUEJ = "9号",
GREENJ = "10号",
}
7、泛型 T(Type)
简单说就是:泛指的类型,不确定的类型,可以理解为一个占位符
(使用T只是习惯,使用任何字母都行)
- K(Key):表示对象中的键类型;
- V(Value):表示对象中的值类型;
- E(Element):表示元素类型。
// T 自定义名称
function myFun<T>(params: T[]) {
return params;
}
myFun <string> (["123", "456"]);
// 定义多个泛型
function join<T, P>(first: T, second: P) {
return `${first}${second}`;
}
join <number, string> (1, "2");
8、断言
断言用来手动指定一个值的类型。值 as 类型
or <类型>值
注意在 tsx 语法中必须使用前者,即 值 as 类型。
function judgeWho(animal: Waiter | Teacher) {
if (animal.anjiao) {
(animal as Teacher).skill();
}else{
(animal as Waiter).say();
}
}
9、in
在做类型保护时间,类似于数组和字符串的 includes
方法
也有遍历的作用,拿到ts类型定义的Key,获取Key还有个方法:keyof是取类型的key的联合类型 , in是遍历类型的key
function judgeWhoTwo(animal: Waiter | Teacher) {
if ("skill" in animal) {
animal.skill();
} else {
animal.say();
}
}
10、类型注解
显式的告诉代码,我们的 count 变量就是一个数字类型,这就叫做类型注解
let count: number; // 类型注解
count = 123;
11、类型推断
- 如果 TS 能够自动分析变量类型, 我们就什么也不需要做了
- 如果 TS 无法分析变量类型的话, 我们就需要使用类型注解
// ts可以推断出count 为number类型
let count = 123;
12、void和never
返回值类型,也算是基础类型。没有返回值的函数: void
function sayHello(): void {
console.log("hello world");
}
如果一个函数是永远也执行不完的,就可以定义返回值为 never
function errorFuntion(): never {
throw new Error();
console.log("Hello World");
}
一个函数有入参,也有出参,项目中的常规写法:
// 定义一个小姐姐
interface IGirl {
name: string,
age: number,
skill: string,
isAnMo: boolean;
number: JiShiEnum;
};
// 定义搜索小姐姐的入参
interface ISearchParams extends IGirl{
serviceTime: string;
}
interface IGetGirls {
data: IGirl[];
}
// 函数主体
export function getGirls(data: ISearchParams): Promise<IGetGirls> {
return axios({
url: `/dabaojian/getGirls`,
method: 'GET',
data,
});
}
13、类型检测
1、typeof
typeof 操作符可以用来获取一个变量或对象的类型
interface Hero {
name: string;
skill: string;
}
const zed: Hero = { name: "影流之主", skill: "影子" };
type LOL = typeof zed; // type LOL = Hero
在上面代码中,我们通过 typeof 操作符获取 zed 变量的类型并赋值给 LOL 类型变量,之后我们就可以使用 LOL 类型
const ahri: LOL = { name: "阿狸", skill: "魅惑" };
2、instanceof
class NumberObj {
count: number;
}
function addObj(first: object | NumberObj, second: object | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count;
}
return 0;
}
3、keyof
keyof 与 Object.keys 略有相似,只不过 keyof 取 interface 的键
interface Point {
x: number;
y: number;
}
// type keys = "x" | "y"
type keys = keyof Point;
用 keyof 可以更好的定义数据类型
function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
return o[name]
}
14、ts类里的关键字
了解ts关键字的作用,在写base类的时候可能会用到,个人用的不多。
- public
- private 类的外部不可用,继承也不行
- protected 类的外部不可用,继承可以
- public readOnly xxx 只读属性
- static funcXXX 静态方法,不需要 new 就可以调用
- abstract funcXXX 抽象类,所有子类都必须要实现 funcXXX
五、tsconfig
需要去了解 tsconfig.json 中一些参数的说明,具体参考官方文档tsconfig.json
1、作用:
- 用于标识 TypeScript 项目的根路径;
- 用于配置 TypeScript 编译器;
- 用于指定编译的文件。
2、注意事项:
- tsc -init 生成 tsconfig.json,项目目录下直接 tsc,编译的时候就会走配置文件
- compilerOptions 内部字段含义 阿宝哥 这篇文章有详细说明
- 项目别名配置:遇到过的一个坑,仅在项目config中配置别名不生效,需要在tsconfig.json中再配置一遍
六、Utility Types
Utility Types: 可以理解为基于ts封装的工具类型;
具体源码解析可以参考:
1、Partial<T>
将T中所有属性转换为可选属性。返回的类型可以是T的任意子集
export interface UserModel {
name: string;
age?: number;
sex: number;
}
type JUserModel = Partial<UserModel>
// =
type JUserModel = {
name?: string | undefined;
age?: number | undefined;
sex?: number | undefined;
}
// 源码解析
type Partial<T> = { [P in keyof T]?: T[P]; };
2、Required<T>
通过将T的所有属性设置为必选属性来构造一个新的类型。与Partial相反
type JUserModel2 = Required<UserModel>
// =
type JUserModel2 = {
name: string;
age: number;
sex: number;
}
3、Readonly<T>
将T中所有属性设置为只读
type JUserModel3 = Readonly<UserModel>
// =
type JUserModel3 = {
readonly name: string;
readonly age?: number | undefined;
readonly sex: number;
}
4、Record<K,T>
构造一个类型,该类型具有一组属性K,每个属性的类型为T。可用于将一个类型的属性映射为另一个类型。Record 后面的泛型就是对象键和值的类型。
简单理解:K对应对应的key,T对应对象的value,返回的就是一个声明好的对象
type TodoProperty = 'title' | 'description';
type Todo = Record<TodoProperty, string>;
// =
type Todo = {
title: string;
description: string;
}
interface IGirl {
name: string;
age: number;
}
type allGirls = Record<string, IGirl>
5、Pick<T,K>
在一个声明好的对象中,挑选一部分出来组成一个新的声明对象
interface Todo {
title: string;
description: string;
done: boolean;
}
type TodoBase = Pick<Todo, "title" | "done">;
// =
type TodoBase = {
title: string;
done: boolean;
}
6、Omit<T,K>
从T中取出除去K的其他所有属性。与Pick相对。
7、Exclude<T,U>
从T中排除可分配给U的属性,剩余的属性构成新的类型
type T0 = Exclude<'a' | 'b' | 'c', 'a'>;
// =
type T0 = "b" | "c"
8、Extract<T,U>
从T中抽出可分配给U的属性构成新的类型。与Exclude相反
type T0 = Extract<'a' | 'b' | 'c', 'a'>;
// =
type T0 = 'a'
9、NonNullable<T>
去除T中的 null 和 undefined 类型
10、Parameters<T>
返回类型为T的函数的参数类型所组成的数组
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [string]
11、ReturnType<T>
function T的返回类型
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
12、InstanceType<T>
返回构造函数类型T的实例类型
class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C>; // C
七、ts学习资源
- B站 技术胖ts入门视频 胖哥新版ts教程
- 尚硅谷2021版TypeScript教程(李立超老师TS新课) 还算比较新,喜欢视频学习的同学了解下
- TypeScript 中文手册 比官网那个易读一些
- TypeScript与React结合 快速上手指南
- 一份不可多得的 TS 学习指南(1.8W字) 阿宝哥,ts大佬 主页有很多ts教程
- 深入理解 TypeScript 讲的就比较深入了
- TypeScript 代码整洁之道 翻译国外大佬写的,国内大佬翻译的
- TypeScript Playground TypeScript 官方提供的在线 TypeScript 运行环境
- json2ts 将JSON转换成ts声明,应该好用,不过我们后端的接口文档自带了这个功能,我是用不上了。了解到有些类库可以直接根据数据表结构生成ts定义
八、往期回顾
- 一名 vueCoder 总结的 React 基础 180+ 👍🏿
- Vue 转 React不完全指北 600+ 👍🏿
- 跳槽人速来,面经&资源分享 1100+ 👍🏿
- 一年半前端人的求职路 300+ 👍🏿
- vue2.x高阶问题,你能答多少 400+ 👍🏿
- 聊一聊前端性能优化 1300+ 👍🏿
- Egg + Puppeteer 实现Html转PDF(已开源) 50+ 👍🏿
- web打印,一篇搞定 15+ 👍🏿