TypeScript上手指南

2022/9/1 TypeScript

源链接:https://juejin.cn/post/6981728323051192357 (opens new window)

# 一、前言

# 二、TypeScript 的优缺点

# 1. 优点

  • 代码的可读性和可维护性,友好的提示。
  • 在编译阶段就发现大部分错误,避免了很多线上 bug。
  • 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等。

# 2. 缺点

  • 有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念。
  • 会增加一些开发成本,当然这是前期的,后期维护更简单了。
  • 一些 JavaScript 库需要兼容,提供声明文件。
  • TypeScript 编译是需要时间的,这就意味着项目大了以后,开发环境启动生产环境打包的速度就成了考验。

# 三、anyScript

可能因为业务场景或者业务紧张,or 某个跑路的大哥省了点功夫,用了 TypeScript 的项目也可能会变成 anyScript。以下是几种救急的方式:

  • // @ts-nocheck 禁用整个文件的 ts 校验;
  • // @ts-ignore 禁用单行 ts 校验;
  • anyunknown 不建议多用,但也不是不能用,有些场景确实不好写 ts 定义。这个时候就不要硬憋自己了,写个备注 any 下。

抛个面试题:你知道 any 和 unknown 的区别吗?

# 四、TypeScript 类型

# 1. 基础类型

  • 常用:boolean、number、string、array、enum、any、void。
  • 不常用:tuple、null、undefined、never。
const count: number = 20220901;
1

# 2. 对象类型

  • 简单理解 interface 和 type 的区别:type 更强大,interface 可以进行声明合并,type 不行。
interface Hero {
  name: string;
  age: number;
  skill: string;
  skinNum?: number;
  say(): string; // say 函数返回值为 string
  [propname: string]: any; // 当前 Hero 可定义任意字符串类型的 key
}

type Hero = {
  name: string,
  age: number,
  skill: string,
  skinNum?: number,
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 3. 数组类型

  • 项目中常见的写法,需要声明列表数据类型:
interface Item {
  id: number;
  name: string;
}
const objectArr: Item[] = [{ id: 1, name: 'wen' }];
const objectArr: Array<Item> = [{ id: 1, name: 'wen' }];

const numberArr: number[] = [1, 2, 3];
const arr: (number | string)[] = [1, "string", 2];
1
2
3
4
5
6
7
8
9

# 4. 元组 tuple

  • 元组和数组类似,但是类型注解时会不一样。
  • 赋值的类型、位置、个数需要和定义(声明)的类型、位置、个数一致。
// 数组 某个位置的值可以是注解中的任何一个
const LOL: (string | number)[] = ["zed", 25, "darts"];

// 元祖 每一项数据类型必须一致
const LOL: [string, string, number] = ["zed", "darts", 25];
1
2
3
4
5

# 5. 联合|类型 or 交叉&类型

  • 联合类型:某个变量可能是多个 interface 中的其中一个,用 | 分割。
  • 交叉类型:由多个类型组成,用 & 连接。
type UnionA = 'px' | 'em' | 'rem' | '%';
type UnionB = 'vh' | 'em' | 'rem' | 'pt';
type IntersectionUnion = UnionA & UnionB;

const intersectionA: IntersectionUnion = 'em'; // ok
const intersectionB: IntersectionUnion = 'rem'; // ok
const intersectionC: IntersectionUnion = 'px'; // ts(2322)
const intersectionD: IntersectionUnion = 'pt'; // ts(2322)
1
2
3
4
5
6
7
8

# 6. enum 枚举

  • 提高代码可维护性,统一维护某些枚举值。
enum TestEnum {
  RED,
  BLUE,
  GREEN
}
enum TestEnum2 {
  RED = 6,
  BLUE,
  GREEN
}

const test: TestEnum = TestEnum.BLUE // 1
const test2: TestEnum2 = TestEnum2.BLUE // 7
1
2
3
4
5
6
7
8
9
10
11
12
13

# 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");
1
2
3
4
5
6
7
8
9
10
11

# 8. 断言

  • 断言用来手动指定一个值的类型。值 as 类型 or <类型>值

注意在 tsx 语法中必须使用前者,即 值 as 类型

# 9. in

  • 类似于数组和字符串的 includes 方法。
  • 也有遍历的作用,拿到 ts 类型定义的 key;获取 key 还有个方法:keyof 是取类型的 key 的联合类型 , in 是遍历类型的 key。
interface test {
  one: string,
  two?: string,
  three?: string
}
const t: test = {
  one: 'one'
}
console.log('one' in t) // true
1
2
3
4
5
6
7
8
9

# 10. 类型注解

  • 显式的告诉代码,count 变量就是一个数字类型,这就叫做类型注解。
let count: number; // 类型注解
count = 123;
1
2

# 11. 类型推断

  • 如果 TS 能够自动分析变量类型, 我们就什么也不需要做了。
  • 如果 TS 无法分析变量类型的话, 我们就需要使用类型注解。
// ts 可以推断出 count 为 number 类型
let count = 123;
1
2

# 12. void 和 never

  • 返回值类型,也算是基础类型。
// 没有返回值的函数 void
function sayHello(): void {
  console.log("hello");
}

// 如果一个函数是永远也执行不完的,就可以定义返回值为 never
function errorFuntion(): never {
  throw new Error();
  console.log("error");
}
1
2
3
4
5
6
7
8
9
10

# 13. 类型检测

# 13.1 typeof

  • typeof 操作符可以用来获取一个变量或对象的类型。
interface Hero {
  name: string;
  skill: string;
}
const zed: Hero = { name: "影流之主", skill: "影子" };
type LOL = typeof zed; // type LOL = Hero
const ahri: LOL = { name: "阿狸", skill: "魅惑" };
1
2
3
4
5
6
7

# 13.2 instanceof

# 13.3 keyof

  • keyof 与 Object.keys 略有相似,只不过 keyof 取 interface 的键。
interface Point {
    x: number;
    y: number;
}
type keys = keyof Point; // type keys = "x" | "y"

// 用 keyof 可以更好的定义数据类型
function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
  return o[name]
}
1
2
3
4
5
6
7
8
9
10

# 14. TypeScript 类里的关键字

了解 ts 关键字的作用,在写 base 类的时候可能会用到,个人用的不多。

  • public
  • private 类的外部不可用,继承也不行
  • protected 类的外部不可用,继承可以
  • public readOnly xxx 只读属性
  • static funcXXX 静态方法,不需要 new 就可以调用
  • abstract funcXXX 抽象类,所有子类都必须要实现 funcXXX

# 五、tsconfig

# 1. 作用

  • 用于标识 TypeScript 项目的根路径;
  • 用于配置 TypeScript 编译器;
  • 用于指定编译的文件。

# 2. 注意事项

  • tsc -init 生成 tsconfig.json,项目目录下直接 tsc,编译的时候就会走配置文件。
  • compilerOptions 内部字段含义 阿宝哥 这篇文章有详细说明 (opens new window)
  • 项目别名配置:遇到过的一个坑,仅在项目config中配置别名不生效,需要在 tsconfig.json 中再配置一遍。

# 六、Utility Types

# 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]; };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2. Required<T>

  • 通过将 T 的所有属性设置为必选属性来构造一个新的类型。与 Partial 相反。
type JUserModel2 = Required<UserModel>
// =
type JUserModel2 = {
  name: string;
  age: number;
  sex: number;
}
1
2
3
4
5
6
7

# 3. Readonly<T>

  • 将 T 中所有属性设置为只读
type JUserModel3 = Readonly<UserModel>
// =
type JUserModel3 = {
  readonly name: string;
  readonly age?: number | undefined;
  readonly sex: number;
}
1
2
3
4
5
6
7

# 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;
}
1
2
3
4
5
6
7

# 5. Pick<T,K>

  • 在一个声明好的对象中,挑选一部分出来组成一个新的声明对象。
interface Todo {
  title: string;
  description: string;
  done: boolean;
}
type TodoBase = Pick<Todo, "title" | "done">;
// =
type TodoBase = {
  title: string;
  done: boolean;
}
1
2
3
4
5
6
7
8
9
10
11

# 6. Omit<T,K>

  • 从 T 中取出除去 K 的其他所有属性。与 Pick 相对。

# 7. Exclude<T,U>

  • 从 T 中排除可分配给 U 的属性,剩余的属性构成新的类型。
type T0 = Exclude<'a' | 'b' | 'c', 'a'>; 
// = 
type T0 = "b" | "c"
1
2
3

# 8. Extract<T,U>

  • 从 T 中抽出可分配给 U 的属性构成新的类型。与 Exclude 相反。
type T0 = Extract<'a' | 'b' | 'c', 'a'>; 
// = 
type T0 = 'a'
1
2
3

# 9. NonNullable<T>

  • 去除 T 中的 null 和 undefined 类型。

# 10. Parameters<T>

  • 返回类型为 T 的函数的参数类型所组成的数组。
type T0 = Parameters<() => string>;  // []
type T1 = Parameters<(s: string) => void>;  // [string]
1
2

# 11. ReturnType<T>

  • function T 的返回类型。
type T0 = ReturnType<() => string>;  // string
type T1 = ReturnType<(s: string) => void>;  // void
1
2

# 12. InstanceType<T>

  • 返回构造函数类型 T 的实例类型。
class C {
  x = 0;
  y = 0;
}

type T0 = InstanceType<typeof C>;  // C
1
2
3
4
5
6

# 七、TypeScript 学习资源

Last Updated: 2023/04/22, 23:39:26
彩虹
周杰伦