타입스크립트는 초기 배열에 어떤 타입이 들어있는지 기억하고, 배열이 해당 데이터 타입에서만 작동하도록 제한한다. 이런 방식으로 배열의 데이터 타입을 하나로 유지시킨다.
배열 타입 애너테이션은 요소 타입 다음에 []
을 작성한다.
let numbers: number[];
numbers = [1, 2, 3];
📌
Array<number>
으로 작성할 수도 있지만, 대부분은 더 간단한number[]
을 선호한다고 한다.
괄호를 사용해서 애너테이션의 함수 반환 부분과 배열 타입 묶음을 나타낼 수 있다.
// string 배열을 반환하는 함수 (함수 타입)
let createString: () => string[];
// string을 반환하는 함수 배열 (배열 타입)
let stringCreater: (() => string)[];
괄호를 사용해서 애너테이션의 배열 콘텐츠와 유니언 타입 묶음을 나타낼 수 있다.
// string 또는 number 배열
let stringOrArrayOfNumbers: string | number[];
// string 또는 number인 요소의 배열
let arrayOfStringOfNumbers: (string | number)[];
초기에 빈 배열로 선언된 변수에서 타입 애너테이션을 포함하지 않으면 타입스크립트는 배열을 any[]
로 취급한다.
any 타입 변수와 마찬가지로 any 타입 배열은 모든 타입을 허용하기 때문에 타입 검사의 이점을 누릴 수 없다.
let array = []; // any[]
array.push(''); // string[]
array.push(0); // (string | number)[]
다차원 배열은 배열 깊이만큼 []
를 작성한다.
// 배열
let one: number[];
// 2차원 배열
let two: number[][];
two = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
타입스크립트는 배열의 멤버를 찾아서 해당 배열의 타입 요소를 되돌려주는 인덱스 기반 접근 방식을 이해하는 언어
const stringArr = ['s', 't', 'r']; // string[]
const str = stringArr[0]; // string
유니언 타입으로 된 배열의 멤버는 동일하게 유니언 타입이다.
const stringOrNumber = ['s', 't', 'r', 1, 2, 3]; // (string | number)[]
const num = stringOrNumber[0]; // string | number
타입스크립트는 접근하려는 배열의 멤버가 존재하는지 확인하지 않는다.
그래서 배열의 길이보다 더 큰 인덱스에 접근해도 오류를 표시하지 않는다.
const numbers = [1, 2, 3, 4, 5];
console.log(numbers[0]); // 1
console.log(numbers[4]); // 5
console.log(numbers[100]); // undefined
const num100 = numbers[100]; // number
위의 예시에서 numbers배열의 100번째 인덱스에 접근해도 오류를 표시하지 않는다. 그리고 numbers[100]
을 할당한 변수 num100
의 타입은 undefined가 아니라 number로 간주된다.
📌 배열 조회를 더 제한하고 타입을 안전하게 만드는 noUncheckedIndexedAccess 플래그가 있지만, 이 플래그는 엄격해서 대부분의 프로젝트에서 사용하지 않는다고 한다.
스프레드 연산자를 사용하면 배열을 결합할 수 있다.
// 동일한 타입의 배열을 결합
const numbers1 = [1, 1, 1]; // number[]
const numbers2 = [2, 2, 2]; // number[]
const mixNumbers = [...numbers1, ...numbers2]; // number[]
// 서로 다른 타입의 배열 결합
const numbers = [1, 1, 1]; // number[]
const strings = ['str', 'str', 'str']; // string[]
const mixNumbersAndStrings = [...numbers, ...strings]; // (string | number)[]
나머지 매개변수에 전달할 인수 배열은 나머지 매개변수와 동일한 배열 타입을 가져야 한다.
function add(name: string, ...numbers: number[]) {
// code...
}
const score = [1, 2, 3];
add('James', 1, 2, 3); // OK
add('James', ...score); // OK
add('Nora', true, true, false); // Error
튜플(Tuple): 고정된 타입과 크기를 갖는 배열
👉 유니언 타입보다 더 구체적이다.
let numberAndString: [number, string];
numberAndString = [100, '100']; // OK
numberAndString = [true, '100'] // Error: Type 'boolean' is not assignable to type 'number'.
numberAndString = [100];
// Error: Type '[number]' is not assignable to type '[number, string]'.
// Source has 1 element(s) but target requires 2.
한 번에 여러 값을 할당하기 위해 튜플과 배열 구조 분해 할당을 함께 사용할 수도 있다.
// num: number
// str: string
let [num, str] = Math.random() > 0.5
? [0, 'hello']
: [1, 'world'];
num = 'hi~'; // Error: Type 'string' is not assignable to type 'number'.
가변 길이의 배열 타입은 튜플 타입에 할당할 수 없다.
let oneAndTrue = [1, true]; // (number | boolean)[]
oneAndTrue = [12]; // OK
oneAndTrue = [true]; // OK
oneAndTrue = [111, true, false]; // OK
(number | boolean)[]
이다. 따라서 개수 제한 없이 숫자와 불리언 타입을 담을 수 있다. // (number | boolean)[] 타입은 [number, boolean] 타입에 할당할 수 없다.
const numberAndBoolean: [number, boolean] = oneAndTrue;
// Error: Type '(number | boolean)[]' is not assignable to type '[number, boolean]'.
// Target requires 2 element(s) but source may have fewer.
// OK
const oneAndTrue: [number, boolean] = [1, true];
const numberAndBoolean: [number, boolean] = oneAndTrue;
// Error
const oneAndTrue: [number, boolean, string] = [1, true, 'hello'];
const numberAndBoolean: [number, boolean] = oneAndTrue;
// Error: Type '[number, boolean, string]' is not assignable to type '[number, boolean]'.
// Source has 3 element(s) but target allows only 2.
튜플은 함수에 전달할 인수를 저장할때 유용하다.
아래 예시를 보자. logPair 함수의 매개변수에는 string과 number 타입을 전달할 수 있다. (string | number)[]
타입의 값을 인수로 전달하려고 하면 타입의 안전을 보장할 수 없다.
function logPair(name: string, value: number) {
// code...
}
const pairArray = ['Amage', 1]; // (string | number)[]
logPair(...pairArray); // Error: A spread argument must either have a tuple type or be passed to a rest parameter.
그러나 값이 [string, number]
튜플 타입이라고 알고있으면 값이 일치한다는 것을 알게된다.
function logPair(name: string, value: number) {
// code...
}
const pairArray: [string, number] = ['Amage', 1];
logPair(...pairArray); // OK
나머지 매개변수 튜플을 사용하고 싶다면, 인수 목록을 배열에 저장해 함께 사용할 수 있다.
function logTrio(name: string, value: [number, boolean]) {
// code...
}
const trios: [string, [number, boolean]][] = [
['a', [0, true]],
['b', [1, true]],
['b', [2, true]]
];
trios.forEach(trio => logTrio(...trio)); // OK
trios.forEach(logTrio);
// Error: Argument of type '(name: string, value: [number, boolean]) => void' is not assignable to parameter of type '(value: [string, [number, boolean]], index: number, array: [string, [number, boolean]][]) => void'.
// Types of parameters 'name' and 'value' are incompatible.
// Type '[string, [number, boolean]]' is not assignable to type 'string'.
...trio
와 logTrio
의 매개변수 타입이 일치한다. trios.forEach(logTrio)
는 logTrio
함수의 매개변수에 [string, [number, boolean]]
전체를 전달하려고 시도하기 때문에 할당할 수 없다. 두 가지 방법으로 튜플 타입을 나타낼 수 있다.
1. 명시적 튜플 타입
2. const 어서션(assertion)
애너테이션으로 튜플 타입을 나타낼 수 있다.
// 반환타입: [string, number]
function stringAndNumber(input: string): [string, number] {
return [input, input.length];
}
// user 타입: string
// userLength 타입: number
const [user, userLength] = stringAndNumber('Nara');
const 어서션은 값을 읽기 전용으로 만든다.
// 타입: (number | boolean)[]
const unionArray = [0, false];
unionArray[0] = 100;
// 타입: readonly [1, true]
const readonlyTuple = [1, true] as const;
readonlyTuple[0] = 100; // Error: Cannot assign to '0' because it is a read-only property.
// [number, boolean]은 명시적 튜플 배열 -> 수정 가능
const pairMutable: [number, boolean] = [0, false];
pairMutable[0] = 100; // OK
// as const가 붙은 값은 값이 수정될 수 있는 곳에 할당할 수 없다.
const pairAlsoMutable: [number, boolean] = [0, false] as const;
// The type 'readonly [0, false]' is 'readonly' and cannot be assigned to the mutable type '[number, boolean]'.