러닝 타입스크립트 | ch 6. 배열

doodoo·2023년 3월 4일
0
post-thumbnail

배열 타입

타입스크립트는 초기 배열에 어떤 타입이 들어있는지 기억하고, 배열이 해당 데이터 타입에서만 작동하도록 제한한다. 이런 방식으로 배열의 데이터 타입을 하나로 유지시킨다.

배열 타입 애너테이션은 요소 타입 다음에 [] 을 작성한다.

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 타입 변수와 마찬가지로 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
  • 변수 oneAndTrue의 타입은 (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.
  • 위의 예시처럼 처럼 튜플 배열에 oneAndTrue을 할당하려고 하면 오류가 난다.

// 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'.
  • ...triologTrio의 매개변수 타입이 일치한다.
  • 하지만 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 어서션(assertion)

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.
  • 배열 리터럴 뒤에 const 어서션인 as const 연산자가 매치되면 배열이 튜플로 처리되어야 함을 나타낸다.
  • readonlyTuple은 읽기 전용이므로 0번 인덱스에 값을 할당할 수 없다.

// [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]'.
  • as const는 값이 변경될 수 있는 pairAlsoMutable에 할당할 수 없도록 한다.

0개의 댓글