元组
元组是一个描述定长数组的类型,数组的各项可以类型不同。
考虑下面的 js 代码:
const arr = ['I', 'l', 4, '514']
arr 的数组内含有 string 和 number,而且我们可以确定每个 index 的数据类型,index 是 0,1,3 时候是 string,2 时候是 number,所以它的类型是 [ string, string, number, string ]。
可以写为下述代码:
const arr: [string, string, number, string] = ['I', 'l', 4, '514']
arr[0] = '1' // OK
arr[0] = 1 // Type '1' is not assignable to type 'string'.ts(2322)
const arr2: ['I', 'l', 4, '514'] = ['I', 'l', 4, '514']
const arr3: [unknown, unknown, unknown, string] = ['I', 'l', 4, '514']
由于存在子类型多态(包含多态),同一个值可以有多种类型,所以我们可以有多种对其声明的类型,比如 arr2 和 arr3。
元组是定长的,所以不同长度的元组是不兼容的。这是 ts 2.7 才引入的限制,如果需要变长的元组,请看 Fixed Length Tuples 这次改动的解释。
下面看一段新的代码:
const arr: readonly [string, string, number, string] = ['I', 'l', 4, '514']
arr[0] = '1' // Cannot assign to '0' because it is a read-only property.
这里我们添加了 readonly 修饰符 , 就能保证数据是只读的了。只读性对于用户写代码来说,是非常重要的特性。如果你以后没有修改的需求,请尽可能给数据加上 readonly 修饰符。
当然 readonly 这个词很长,所以我们可以考虑让类型推断自动帮我们加上。 写简洁清晰的 TypeScript 代码的诀窍就是,多用类型推断功能。如果需要推断出来 readonly 的元组,我们应该使用 const contexts for literal expressions :
const arr = ['I', 'l', 4, '514'] as const; // const arr: readonly ["I", "l", 4, "514"]
数组
数组是一个变长的,每一项的类型都相同的列表,数组的写法如下
const foo: Array<number> = [1, 2, 3];
其中 foo 的类型参数是 Array<number>
, 这里的 Array
带一个尖括号,尖括号内的是类型参数。
我们可以这样子想象,Array 是一个类型空间(这里的空间指声明空间,声明空间是笔记一引入的概念)) 上的一个函数,而且是一个构造函数,它的作用是,接受一个 type,返回一个 type。用伪代码,我们可以写成:
Array: (elementType: type) => type
上面的 Array<number>
中:
number 是一个类型,是一个 type, number 就是 elementType 这个形式参数的实际参数,
而 Array<number>
就是一个由 Array 这个一元 类型构造器 传入一个实际类型参数——也就是 number 后生成的新类型。
由于数组过于常用,或者为了和 C# 长得更像,数组有个另类的写法,比如:
const foo: number[] = [1, 2, 3];
两种写法都是合法的。
我个人倾向于,在简单的使用常见中使用 []
,在复杂的场景中(生成的数组类型还作为其他类型构造器的类型参数,或者 Array 的类型参数不是单纯的标识符)使用 Array<>
类型别名
A type alias declaration introduces a type alias in the containing declaration space.
用个比喻说,类型别名 是类型声明空间中的变量和函数。我下面用不专业的说法描述一下:
类型别名变量
类型别名第一个作用是当作类型中的一个变量(类似于 js 的 const 声明的变量)使用
type ID = string;
type Rank = number;
const aliceId: ID = getId('alice');
const bobId: ID = getId('bob');
const aliceRank: Rank = getRank('alice');
const bobRank: Rank = getRank('bob');
你给类型起了一个别名,如果 ID 要从 string 变成 number,或者自定义的 UUID 类,我们都只需要改一处就好。这是类型别名最初的用法。
类型别名函数
类型别名可以当作一个类型的函数使用,比如:
type Pair<A, B> = [A, B];
type List<T> = Array<T>;
type Matrix<T> = List<List<T>>;
type Foo<T> = []
类型别名是可以带类型参数的函数。接受一个或者多个类型,返回一个类型,而且我们的类型参数可以不被使用。