この章では、TypeScriptで配列をどのように型付けできるかを調べます。
配列はJavaScriptにおいて以下の役割を果たすことができます(1つまたは複数の役割を組み合わせることもできます)。
TypeScriptは、これらの2つの役割に対応するために、様々な配列の型付け方法を提供しています。次にそれらを見ていきましょう。
配列型リテラルは、要素型に `[]` を付けたものです。以下のコードでは、配列型リテラルは `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; }
配列の2つの役割のために、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
対照的に、配列リテラルに少なくとも1つの要素がある場合、要素型は固定され、後で変更されません。
// %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` アサーション を追加できます。
// %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` アサーションには2つの潜在的な落とし穴があります。
まず、推論された型は可能な限り狭くなります。これにより、`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` はタプル型が推論されるため、同じ効果があります。