TypeScript 中的类型(下)

何时使用 enum

实例一:用 A.todo 将数字映射成一个有意义的语法

实例二:可以用于表示权限

以上代码中,Manage = Read | Write | Delete 是 JS 中的二进制操作(二进制或运算符),0b 在 JS 中表示二进制

何时用 enum 会显得很呆?

如上所示,将 'apple' 映射为 Fruit.apple 完全是多此一举,还是别再用 enum 来映射字符串了吧ヾ(-_-;)

只推荐用 enum 对数字做映射,除此之外都不推荐使用 enum

何时使用 type

TS 中的 Type 叫作类型别名,Type Aliases,其实就是给其他类型取个名字,示例如下:

1
2
3
4
5
6
7
8
9
10
11
type Name = string;
type FalseLike = '' | 0 | false | null | undefined;
type Point = { x: number; y: number };
type Points = Point[];
type Line = [Point, Point];
type Circle = { center: Point; radius: number };
type Fn = (a: number, b: number) => number;
type FnWithProps = {
(a: number, b: number): number;
prop1: number;
};

如何体现出 type 只是一个别名?

1
2
type X = { x: number }
type A = X
以上 TS 代码中 A 的类型是?

由于 type 只是一个别名,并不是声明了一个真正的类型,此处 A 的类型为 { x: number }

如何理解 type FalseLike = '' | 0 | false | null | undefined

上面的五个值其实都是类型,类型可以理解为集合,因此可以看作五个集合并在一起,每个集合中只包含一个值;另外,不支持 NaN 作为类型,因为 NaN 表示的是一个值而不是类型

带有属性的函数的声明方式

1
2
3
4
5
6
7
8
type FnWithProps = {
(a: number, b: number): number;
props: string;
};

const fn: FnWithProps = (x, y) => x * y;

fn.props = 'hello';

多用于 React 中

何时使用 interface

声明接口,用于描述对象的属性 declare the shapes of objects

1
2
3
4
5
interface Data { [k: string]: string; }
interface Point { x: number; y: number; }
interface Points extends Array<Point> {}
interface Fn { (x: number, y: number): number; }
interface Date2 extends Date {}

type 如何实现继承?

1
2
3
4
5
6
interface X { age: number; }

type Al = Array<string> & { name: string; } & X;

// A1几乎等价于A2
interface A2 extends Array<string>, X { name: string; }

type 和 interface 的区别

区别一

interface 只能描述对象,而 type 能描述所有数据类型

区别二

type 并不会创建一个新的类型,只是创建了一个类型别名;而 interface 则是类型声明,会创建一个新的类型

1
2
3
4
5
type A = string;
type B = A; // B 的类型是 string

interface D extends Date {}
type E = D; // E 的类型是 D

区别三

type 不可重新赋值,因此不能存在同名的 type;而 interface 可以声明同名的类型,同名的 interface 会自动合并

1
2
3
4
5
6
type A = string; // 报错 Duplicate identifier 'A'
type A = number; // 报错 Duplicate identifier 'A'

interface X { name: string; }
interface X { age: number; }
const x: X = { name: 'ClariS', age: 18 }; // X 为 { name: string; age: number; }

使用 type 无法进行类型扩展,因此

对外 API 尽量用 interface,方便用户进行类型扩展;对内 API 尽量用 type,防止代码分散

例如,当你设计一个库或框架的公共 API 时,可以使用接口来定义输入和输出的数据结构,以提供明确的类型信息给用户,并允许用户按照自己的需求扩展接口。

而当你在代码内部定义一些仅在局部范围内使用的类型时,使用类型别名可以让代码更加集中和易于维护。它可以将类型定义与具体的实现逻辑分离开来,使代码更加模块化。

下面是一个扩展 interface 的实例:

扩展 axios

1
2
3
4
5
6
7
import { AxiosRequestConfig } from 'axios';
declare module 'axios' {
export interface AxiosRequestConfig {
_autoLoading?: boolean;
_mock?: string;
}
}

扩展 string

1
2
3
4
5
declare global {
interface String {
padZero(length: number): string;
}
}

区别四

interface 使用 extends 来实现继承,而 type 只能用 & 来模拟继承

1
2
3
4
5
6
7
8
9
interface A {
a: string
}

interface B extends A {
b: string
}

type C = { c: string } & A

void

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Fn = () => void;

const fn: Fn = () => {
return 'xxx'; // 不报错
};

const x = fn();

console.log(x.toString()); // 报错:Property 'toString' does not exist on type 'void'.

console.log((x as any).toString()); // 不报错

function fn1(a: number): void {
return 'xxx'; // 报错:Type 'string' is not assignable to type 'void'.
}

const fn2 = (a: number): void => {
return 'xxx'; // 报错:Type 'string' is not assignable to type 'void'.
};

(●'◡'●)ノ♥