Array
const
断言和类型推断在本章中,我们将研究如何在 TypeScript 中对数组进行类型定义。
数组在 JavaScript 中可以扮演以下角色(单个角色或多个角色混合):
TypeScript 通过提供多种数组类型定义方式来适应这两种角色。接下来我们将逐一介绍。
Array
数组类型字面量由元素类型后跟 []
组成。在下面的代码中,数组类型字面量是 string[]
// Each Array element has the type `string`:
: string[] = ['fee', 'fi', 'fo', 'fum']; const myStringArray
数组类型字面量是使用全局泛型接口类型 Array
的简写形式
: Array<string> = ['fee', 'fi', 'fo', 'fum']; const myStringArray
如果元素类型更复杂,我们需要为数组类型字面量添加括号
|string)[]
(number=> boolean)[] (()
在这种情况下,泛型类型 Array
更适用
<number|string>
Array<() => boolean> Array
如果数组具有固定长度,并且每个元素都有不同的、固定的类型,具体取决于其位置,则可以使用元组类型字面量,例如 [string, string, boolean]
: [string, string, boolean] = ['oui', 'sí', true]; const yes
如果一个接口只有一个索引签名,我们可以将其用于数组
interface StringArray {: number]: string;
[index
}: StringArray = ['Huey', 'Dewey', 'Louie']; const strArr
同时具有索引签名和属性签名的接口只能用于对象(因为索引元素和属性需要同时定义)
interface FirstNamesAndLastName {: number]: string;
[index: string;
lastName
}
: FirstNamesAndLastName = {
const ducks0: 'Huey',
1: 'Dewey',
2: 'Louie',
: 'Duck',
lastName; }
由于数组的两种角色,TypeScript 不可能总是猜到正确的类型。例如,考虑以下分配给变量 fields
的数组字面量
: Fields = [
const fields'first', 'string', true],
['last', 'string', true],
['age', 'number', false],
[; ]
fields
的最佳类型是什么?以下都是合理的选择
type Fields = Array<[string, string, boolean]>;
type Fields = Array<[string, ('string'|'number'), boolean]>;
type Fields = Array<Array<string|boolean>>;
type Fields = [
string, string, boolean],
[string, string, boolean],
[string, string, boolean],
[; ]
type Fields = [
string, 'string', boolean],
[string, 'string', boolean],
[string, 'number', boolean],
[; ]
type Fields = [
<string|boolean>,
Array<string|boolean>,
Array<string|boolean>,
Array; ]
当我们使用非空数组字面量时,TypeScript 默认推断列表类型(而不是元组类型)
// %inferred-type: (string | number)[]
= [123, 'abc']; const arr
唉,这并不总是我们想要的
function func(p: [number, number]) {
;
return p
}// %inferred-type: number[]
= [1, 2];
const pair1
// @ts-expect-error: Argument of type 'number[]' is not assignable to
// parameter of type '[number, number]'. [...]
func(pair1);
我们可以通过向 const
声明添加类型注释来解决这个问题,这可以避免类型推断
: [number, number] = [1, 2];
const pair2func(pair2); // OK
如果我们使用空数组字面量初始化一个变量,则 TypeScript 最初会推断出类型 any[]
,并随着我们进行更改而逐步更新该类型
// %inferred-type: any[]
= [];
const arr1
.push(123);
arr1// %inferred-type: number[]
;
arr1
.push('abc');
arr1// %inferred-type: (string | number)[]
; arr1
请注意,最初推断的类型不受以后发生的事情的影响。
如果我们使用赋值而不是 .push()
,则工作原理相同
// %inferred-type: any[]
= [];
const arr1
0] = 123;
arr1[// %inferred-type: number[]
;
arr1
1] = 'abc';
arr1[// %inferred-type: (string | number)[]
; arr1
相反,如果数组字面量至少有一个元素,则元素类型是固定的,以后不会更改
// %inferred-type: number[]
= [123];
const arr
// @ts-expect-error: Argument of type '"abc"' is not assignable to
// parameter of type 'number'. (2345)
.push('abc'); arr
const
断言和类型推断我们可以在数组字面量后面加上 const
断言
// %inferred-type: readonly ["igneous", "metamorphic", "sedimentary"]
=
const rockCategories 'igneous', 'metamorphic', 'sedimentary'] as const; [
我们声明 rockCategories
不会改变。这会产生以下影响
数组变为 readonly
- 我们不能使用更改它的操作
// @ts-expect-error: Property 'push' does not exist on type
// 'readonly ["igneous", "metamorphic", "sedimentary"]'. (2339)
.push('sand'); rockCategories
TypeScript 推断出一个元组。比较
// %inferred-type: string[]
= ['igneous', 'metamorphic', 'sedimentary']; const rockCategories2
TypeScript 推断字面量类型("igneous"
等),而不是更通用的类型。也就是说,推断的元组类型不是 [string, string, string]
。
以下是更多带有和不带有 const
断言的数组字面量示例
// %inferred-type: readonly [1, 2, 3, 4]
= [1, 2, 3, 4] as const;
const numbers1 // %inferred-type: number[]
= [1, 2, 3, 4];
const numbers2
// %inferred-type: readonly [true, "abc"]
= [true, 'abc'] as const;
const booleanAndString1 // %inferred-type: (string | boolean)[]
= [true, 'abc']; const booleanAndString2
const
断言的潜在陷阱const
断言有两个潜在的陷阱。
首先,推断的类型尽可能窄。这会导致 let
声明的变量出现问题:我们不能分配除用于初始化的元组之外的任何其他元组
= [1, 2] as const;
let arr
= [1, 2]; // OK
arr
// @ts-expect-error: Type '3' is not assignable to type '2'. (2322)
= [1, 3]; arr
其次,无法修改通过 as const
声明的元组
= [1, 2] as const;
let arr
// @ts-expect-error: Cannot assign to '1' because it is a read-only
// property. (2540)
1] = 3; arr[
这既不是优点也不是缺点,但我们需要意识到这一点。
每当我们通过索引访问数组元素时,TypeScript 总是假设索引在范围内(A 行)
: string[] = ['Hello'];
const messages
// %inferred-type: string
= messages[3]; // (A) const message
由于这个假设,message
的类型是 string
。而不是我们可能预期的 undefined
或 undefined|string
。
如果我们使用元组类型,我们会收到错误消息
: [string] = ['Hello'];
const messages
// @ts-expect-error: Tuple type '[string]' of length '1' has no element
// at index '1'. (2493)
= messages[1]; const message
as const
也会产生相同的效果,因为它会导致推断出元组类型。