📗

3장 배열 타입

3.1 기본 배열

타입스크립트에서의 배열은 자바스크립트 배열과 같이 여러 값을 담을 수 있는 데이터 구조입니다. 자바스크립트와 비슷한 구조를 가지지만 추가로 타입을 명시함으로써 안정성을 제공합니다.

3.1.1 배열의 타입

1) number

let numbers: number[] = [1, 2, 3, 4];

2) string

let fruits: string[] = ["apple", "orange", "banana", "grape"];

3) boolean

let booleans: boolean[] = [true, false];

4) 유니온

let unions: (number | string)[] = [1, "one", 10, "ten"];
유니온 배열의 경우, 타입을 다중으로 포함할 수 있어 유연성을 제공합니다.

5) 튜플

let tuples: [number, string][] = [[1, "one"], [10, "ten"]];
튜플 배열은 각 요소에 대한 타입을 미리 정의합니다.

6) any

let anys: any[] = [1, "one", true, {one: 1}];
any 배열은 모든 종류의 데이터를 포함할 수 있습니다. 그러나 타입 안정성이 감소할 수 있으므로 사용 시 주의해야 합니다.

3.1.2 배열의 선언과 초기화

배열을 선언하는 방법은 다음과 같이 크게 두 가지로 나뉩니다.

1) type[]

let animals: string[] = ["dog", "cat", "rabbit", "alpaca"];
요소의 타입을 명시하여 선언합니다. 가장 일반적으로 사용되는 방법이며 콜론(:) 뒤에 배열의 타입과 대괄호([])를 사용하여 선언합니다.

2) Array<type> (제네릭 타입)

let animals: Array<string> = ["dog", "cat", "rabbit", "alpaca"];
Array 뒤에 꺾쇠괄호(<>)를 적고 그 안에 타입을 명시합니다. 이러한 선언 방식은 제네릭(Generic) 타입이라고 합니다.
💡
또한 push 메서드를 사용하여 배열의 요소를 추가할 수도 있습니다.
let numbers: number[] = [1, 2, 3, 4]; numbers.push(5); // numbers 배열에 5 추가 // 출력 console.log(numbers); // [1, 2, 3, 4, 5]

3.1.3 Readonly

배열을 선언할 때 readonly 접근 제한자를 사용하면 해당 배열은 불변성을 가집니다. 한 번 할당된 값은 이후에 변경할 수 없는 읽기 전용 배열이 됩니다.

1) Readonly 사용 예제

readonly 또한 아래 두 가지 방법을 통해 사용할 수 있습니다.
// readonly Type[] const arrReadonly1: readonly string[] = ["a", "b", "c"]; arrReadonly1[0] = "d"; // Error // readonly Array<Type> const arrReadonly2: ReadonlyArray<string> = ["a", "b", "c"]; arrReadonly2[0] = "f"; // Error
Array<type>type[]로 줄여서 쓰는 것처럼, ReadonlyArray<type> 또한 readonly type[] 형식으로 단축하여 사용이 가능합니다. 또한 readonly 타입인 경우 불변성을 가지고 있기 때문에 변경 시도 시 에러가 발생합니다.

2) Readonly 주의 사항과 할당 규칙

readonly또는 ReadonlyArray는 변경해서는 안 되는 배열을 설명하는 특수 타입입니다. 주의할 점은 읽기 전용 접근 제한자(Readonly Modifier)가 있는 배열은 일반 배열에 할당이 가능하지만, 일반 배열은 읽기 전용 배열에 할당할 수 없습니다.
let arrReadonly3: readonly string[] = ['a','b']; let arrNormal: string[] = ['c']; arrReadonly3.push("hello"); // Error: 'readonly string[]' arrReadonly3 = arrNormal; // ok console.log(arrReadonly3);// ["c"] arrNormal = arrReadonly3; // Error: 'readonly'
 

3.2 튜플 타입

3.2.1 튜플의 선언과 할당

튜플(tuple) 타입은 배열 타입의 또 다른 종류로써, 포함된 요소(Elements 또는 Items)의 수와 특정 위치에 포함된 타입을 정확히 알 수 있습니다. 변수를 정의할 때 튜플의 타입 작성 방법은 [Type1, Type2, Type3]와 같이 대괄호([]) 안에 고려하는 타입의 순서에 맞게 써 줍니다. 문자열과 논리형을 아이템으로 가지는 튜플 타입은 아래와 코드와 같이 작성해 줄 수 있습니다.
let tuple: [string, boolean] = ['a', true];
타입 시스템에서 앞서 정의했던 tuple 변수는 0번 인덱스(Index)에 문자열이 들어있고, 1번 인덱스에 숫자가 들어 있는 배열을 의미합니다. 튜플은 일반 배열 타입과 달리 포함된 아이템의 수와 위치가 중요한 타입이므로, 타입과 다른 값을 할당하거나 index를 초과하여 조회 및 수정하면 에러가 발생합니다.
tuple = [true, 'a']; // Error tuple[2] = 'two'; // Error: no element

3.2.2 튜플의 구조 분해 할당

튜플 타입은 자바스크립트의 배열 구조 분해 할당(Destructuring) 구문을 사용할 수 있습니다.
const stringNumberPair: [string, number] = ['a', 1]; const [inputString, inputNumber] = stringNumberPair; // Array destructuring console.log(inputString); // 'a' console.log(inputNumber); // 1

3.2.3 튜플의 변경

정해진 타입의 순서와 고정된 길이의 배열을 표현하는 튜플은 할당(Assign)에 국한됩니다. 배열의 .push(), .pop(), .splice() 등 메서드를 통해 값을 추가 또는 제거하는 행위는 막을 수 없습니다. 물론 튜플 정의에 없는 다른 타입을 추가할 수는 없습니다.
console.log(stringNumberPair); // ["a", 1] stringNumberPair.push(100); console.log(stringNumberPair); // ["a", 1, 100] stringNumberPair.splice(1); console.log(stringNumberPair); // ["a"] stringNumberPair.pop(); console.log(stringNumberPair); // [] stringNumberPair.push(true); // Error: type 'string | number'

3.2.4 튜플 타입의 선택적 속성

튜플 타입은 물음표(?) 기호를 사용하여 선택적 속성을 가질 수 있습니다. 주의할 점은 선택적 튜플 요소는 가장 마지막 요소에만 올 수 있으며 길이 속성에도 영향을 준다는 것입니다. 물음표(?)가 붙은 마지막 배열의 요소는 특정 타입 또는 undefined 타입을 가집니다. 즉, 변수 선언 시에 마지막 요소에 대응되는 값이 할당되지 않아도 tsc 에러가 발생하지 않으며, undefined가 할당됩니다.
const Either2Or3: [number, number, number?] = [1, 2]; // const Either2Or3: [number, number, (number | undefined)?] console.log(Either2Or3[2]); // undefined // (property) length: 2 | 3 console.log(Either2Or3.length); // 2

3.2.5 튜플과 rest 연산자

튜플에는 배열/튜플 유형과 함께 spread 또는 rest 연산자(...)를 사용할 수 있습니다. 튜플의 이런 기능은 최소 원소 수를 지정할 수 있음과 동시에 다양한 수의 인수를 받을 수 있고, 코드를 간결하게 작성할 수 있습니다.
const stringNumberBooleans: [string, number, ...boolean[]] = ['a', 1, true, false]; const stringBooleansNumber: [string, ...boolean[], number] = ['a', true, false, 1]; const BooleansStringNumber: [...boolean[], string, number] = [true, false, 'a', 1];
아직 배우진 않았지만, 튜플의 구조 분해 할당과 rest 연산자의 조합은 몇 개가 들어올지 모르는 함수의 매개변수에 유용하게 사용할 수 있습니다.
function myFnc (...args: [string, number, ...boolean[]]) { // name: string, version: number, ...input: boolean[] const [name, version, ...input] = args; // ... }

3.2.6 읽기 전용 튜플

배열 타입과 마찬가지로 튜플 타입에도 readonly 접근 제한자를 사용할 수 있습니다.
const tupleReadonly: readonly [string, number] = ['a', 1]; tupleReadonly[0] = 'two'; // Error : readonly property
튜플은 대부분의 코드에서 생성된 후 변경되지 않는 경향이 있습니다. 그렇기 때문에 가능하면 읽기 전용 튜플로 생성하여 객체를 어떻게 사용해야 하는지에 대한 의도를 개발 단계에서 담아주는 것이 좋습니다.

3.3 다차원 배열 타입

배열 내에 다른 배열이 포함된 형태를 다차원 배열 타입이라고 합니다. 다차원 배열은 배열 타입을 표기한 뒤 차원의 개수에 맞춰 대괄호([])로 명시합니다.

3.3.1 다차원 배열의 선언과 초기화

1) 2차원 배열

let array2D: number[][] = [ [1, 2, 3], [4, 5, 6], ];

2) 3차원 배열

let array3D: number[][][] = [ [ [1, 2, 3], [4, 5, 6], ], [ [7, 8, 9], [10, 11, 12], ] ];

3) n차원 배열

let arraynD: number[][]...[] = [ ..., ... ];
2차원, 3차원 배열과 마찬가지로 중첩하여 n차원 배열을 생성할 수 있습니다.

3.3.2 다차원 배열에서 여러 타입 사용

1) 2차원 배열 / number, string 타입 사용 (유니온 타입)

let arrUnion: (number | string)[][] = [ [1, "apple", 2], ["orange", 3, 4], [5, "grape", 6], ];
number 타입과 string 타입을 동시에 사용하는 경우 타입 사이에 또는 기호(|)를 넣어 유니온 타입으로 사용할 수 있습니다.

2) 3차원 배열 / boolean, 사용자 정의 타입 사용

// 사용자 정의 Type type Fruits = { name: string; count: number; }; let arrMix: (boolean | Fruits)[][][] = [ [ [true, false, true], [{name: "apple", count: 3}], [{name: "banana", count: 1}], ], [ [false, false, true], [{name: "strawberry", count: 10}], [{name: "mango", count: 4}], ] ];
이처럼 boolean 타입과 사용자 정의 타입을 혼합하여 사용하는 것도 가능합니다.

3) 튜플을 요소로 갖는 배열

배열 타입과 튜플 타입을 조합할 수도 있습니다. 아래 예시는 튜플 타입을 요소로 가지는 2차원 배열 타입입니다. 다차원 배열에서 살펴보았듯 배열의 차원을 더 늘리고 싶다면 대괄호([])를 덧붙여나가면 됩니다.
// 2D Array const tupleInArr: [number, string, boolean][] = [ [1, 'a', true], [2, 'b', false], [3, 'c', true], ];

3.3.3 다차원 튜플

1) 튜플을 요소로 갖는 다차원 튜플

튜플 타입을 요소로 가지는 2, 3, 4차원 튜플을 정의합니다. 다차원 튜플은 대괄호([])를 덧붙여 나가던 방식과 조금 달리 차원이 올라갈수록 대괄호([])를 덧씌워주며 작성하면 됩니다. 아래 코드 예시에서 [...number[]]는 [number, number, number]와 동일한 의미를 가집니다.
const tuple2D: [[...number[]], [...string[]]] = [ [1, 2, 3], ['a', 'b', 'c'], ]; const tuple3D: [[[...number[]], [...string[]]]] = [[ [1, 2, 3], ['d', 'e', 'f'], ]]; const tuple4D: [[[[...number[]], [...string[]]],[[...number[]], [...string[]]]]] = [[ [ [1, 2, 3], ['a', 'b', 'c'], ], [ [7, 8, 9], ['d', 'e', 'f'], ] ]]; console.log(tuple2D); // [[1, 2, 3], ["a", "b", "c"]] console.log(tuple3D); // [[[1, 2, 3], ["a", "b", "c"]]] console.log(tuple4D); // [[[[1, 2, 3], ["a", "b", "c"]], [[7, 8, 9], ["d", "e", "f"]]]] console.log(tuple4D.length); // 1, [Array(2)] console.log(tuple4D[0].length); // 2, [Array(2), Array(2)] console.log(tuple4D[0][0].length); // 2, [Array(3), Array(3)] console.log(tuple4D[0][0][0].length); // 3, [1, 2, 3]