TypeScript 中的类型(上)

JS/TS 中有哪些数据类型

用集合来理解 TS 数据类型

number 可看做 1、1.1、2、2.3 等所有数字值组成的集合
string 可看做 a、b、ab、c、abc 等所有字符串值组成的集合
boolean 可看做 truefalse 这两个值组成的集合

JS 中的 NumberStringBoolean 只用于包装对象,一般不会用到它们;同样地在 TS 中也不会用到它们。

TS 中一般不会用 Object 来描述对象的类型,因为 Object 所表示的类型范围太大了

如何用 TS 来描述对象

  1. class/constructor 描述
  2. type/interface 描述

描述普通对象

由于 object/Object 太不精确,所以一般使用索引签名Record 泛型来描述普通对象

使用索引签名

思考题:key 的类型可以不是 string 吗?

经测试,k 的类型只能为 numberstringsymbol 这三种

使用 Record

使用具体的字段

1
2
3
4
type A = {
name: string;
age: number;
};

描述数组对象

由于 Array 太不精确,所以一般用 Array<?>string[][string, number] 来描述数组

思考题

1
2
type A = [1, 2, 3]
const a: A = ??????
以上 TS 代码中 a 的值是?

当于只有一个元素的集合,因此 a 的值只能是 [1, 2, 3]

描述函数对象

由于 Function 太不精确,所以一般用 () => ? 来描述函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type FnA = (a: number, b: number) => number;

type FnReturnVoid = (s: string) => void;

type FnReturnUndefined = (s: string) => undefined;

const v: FnReturnVoid = (s: string) => {
console.log(s);
};

const u: FnReturnUndefined = (s: string) => {
console.log(s);
return undefined;
};

带有 this 的函数声明

1
2
3
4
5
6
7
8
9
10
11
12
type Person = {name: string, age: number, sayHi: FnWithThis}
type FnWithThis = (this: Person, name: string) => void

const sayHi: FnWithThis = function(name) { // 此处不能使用箭头函数,因为箭头函数内不存在 this
console.log("hi", this.name)
}

const x: Person = {name: "vivy", age: 18, sayHi}

// 如果函数声明中有 this,那么调用函数时必须显示地传递这个 this
x.sayHi("Citrus")
sayHi.call(x, "Citrus")

描述其他对象

直接用 class(构造函数) 描述即可

1
2
3
4
5
6
7
8
9
10
11
12
13
const d: Date = new Date();

const r: RegExp = /ab+c/;

const m: Map<string, number> = new Map();
m.set('xxx', 1);

const wm: WeakMap<{ name: string }, number> = new WeakMap();

const s: Set<{ name: string }> = new Set();
s.add({ name: 'ClariS' });

const ws: WeakSet<string[]> = new WeakSet();

any、unknown、never 是什么

从集合的角度理解:any 是全集,never 是空集,unknown 是未知集

TS 的绝大部分规则对 any 类型不生效(TS 不报错)

可对 unknown 进行断言,适用于值是从外部获取的,没办法提前知道类型的情况;比如当使用 ajax 获取数据时,后台返回的数据格式不确定,此时可将接口响应的数据类型可定义为 unknown,然后再对其进行断言

1
2
const a: unknown = await ajax.get('/api/users')
const b = a as number

never 一般不会用来声明类型,而是用来进行类型推断

1
2
3
4
5
6
7
8
9
10
11
12
13
type A = string | number | boolean;

const a: A = 'hello' as any;

if (typeof a === 'string') {
a.split('');
} else if (typeof a === 'number') {
a.toFixed(2);
} else if (typeof a === 'boolean') {
a.valueOf();
} else {
a.toString(); // 报错:Property 'toString' does not exist on type 'never'
}

JS 类型与 TS 类型的区别

我是这么理解的:

JS 这门语言其实并没有提供类型相关的关键字(如小写的 numberstringboolean),只是提供了typeof让我们获取变量的类型,所以我认为 JS 对类型非常不重视。

而 TS 则将 JS 中的类型提升到很高的位置,高到如果你把类型写错了代码就不能通过编译。因此这两门语言中的类型其实没有可比性,只是看起来有重叠部分而已。如果一定要比较,我会这么说:

  1. 粒度不同:在 JS 中的类型 number 不可再细分,而 TS 中的 number 可以看做 1、1.1、2、2.3 等无数个小类型的联合
  2. 可变性不同:JS 中的变量类型是可变的,一个 number 随时可以变成一个 string;TS 中除了 any,其他类型要么是不可变的,要么就算可变也是有限制的

(●'◡'●)ノ♥