배열은 같은 타입의 가변 길이 컬렉션이고, 튜플은 ** 각 위치별 타입이 고정된** 배열입니다.

기본 타입 편에서 배열과 튜플을 간단히 다뤘는데, 이번에는 Rest Element, Variadic Tuple, as const 등 심화 내용을 정리합니다.

배열 타입 심화

읽기 전용 배열

TYPESCRIPT
// readonly 배열 — 수정 메서드 사용 불가
const numbers: readonly number[] = [1, 2, 3];
// numbers.push(4);    // ❌ Error
// numbers[0] = 10;    // ❌ Error
// numbers.length = 0; // ❌ Error

// ReadonlyArray<T> — 동일
const names: ReadonlyArray<string> = ['홍길동', '김철수'];

// readonly 배열은 일반 배열에 할당 불가
// const mutable: number[] = numbers; // ❌ Error
// 반대는 가능
const readOnly: readonly number[] = [1, 2, 3] as number[];

다차원 배열

TYPESCRIPT
// 2차원 배열
const matrix: number[][] = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
];

// 타입 별칭으로 가독성 높이기
type Matrix = number[][];
type Grid<T> = T[][];

const boolGrid: Grid<boolean> = [
  [true, false],
  [false, true],
];

튜플 타입 심화

이름이 있는 튜플 요소(Labeled Tuples)

TypeScript 4.0부터 튜플 요소에 이름을 붙일 수 있습니다.

TYPESCRIPT
// 이름 없는 튜플 — 의미를 파악하기 어려움
type OldRange = [number, number];

// 이름 있는 튜플 — 가독성 향상
type Range = [start: number, end: number];

const range: Range = [0, 100];
// IDE에서 range[0]에 마우스를 올리면 'start'라는 힌트가 보임

선택적 튜플 요소

TYPESCRIPT
type FlexiblePoint = [x: number, y: number, z?: number];

const point2D: FlexiblePoint = [10, 20];       // OK
const point3D: FlexiblePoint = [10, 20, 30];   // OK

Rest Element가 있는 튜플

TYPESCRIPT
// 처음 두 요소는 고정, 나머지는 가변
type LogEntry = [timestamp: Date, level: string, ...messages: string[]];

const entry: LogEntry = [
  new Date(),
  'ERROR',
  '서버 연결 실패',
  '재시도 중...',
  '3번째 시도',
];

Rest Element는 튜플의 ** 처음, 중간, 끝** 어디에든 올 수 있습니다.

TYPESCRIPT
// 중간에 Rest Element
type Sandwich = [bread: string, ...fillings: string[], bread2: string];
const sub: Sandwich = ['호밀', '치즈', '상추', '토마토', '호밀'];

// 처음에 Rest Element
type Trailing = [...heads: string[], last: number];
const t: Trailing = ['a', 'b', 'c', 42];

Variadic Tuple Types

TypeScript 4.0에서 도입된 기능으로, 제네릭으로 튜플을 조작할 수 있습니다.

TYPESCRIPT
// 두 튜플을 합치는 타입
type Concat<A extends unknown[], B extends unknown[]> = [...A, ...B];

type Result = Concat<[1, 2], [3, 4]>; // [1, 2, 3, 4]

// 튜플 앞에 요소 추가
type Prepend<T, Arr extends unknown[]> = [T, ...Arr];
type WithId = Prepend<number, [string, boolean]>; // [number, string, boolean]

// 실전 활용: 타입 안전한 함수 합성
function concat<A extends unknown[], B extends unknown[]>(
  a: [...A],
  b: [...B]
): [...A, ...B] {
  return [...a, ...b];
}

const result = concat([1, 'hello'] as const, [true, 42] as const);
// readonly [1, 'hello', true, 42]

as const와 튜플

as const는 배열을 ** 읽기 전용 튜플 **로 변환합니다.

TYPESCRIPT
// 일반 배열 — number[]로 추론
const numbers = [1, 2, 3];

// as const — readonly [1, 2, 3]으로 추론
const tuple = [1, 2, 3] as const;

// 객체 안의 배열도 마찬가지
const config = {
  methods: ['GET', 'POST', 'PUT'],  // string[]
} as const;
// config.methods: readonly ['GET', 'POST', 'PUT']

as const의 실전 활용

TYPESCRIPT
// 라우트 정의에서 활용
const routes = ['/', '/about', '/contact'] as const;
type Route = (typeof routes)[number]; // '/' | '/about' | '/contact'

// 함수 매개변수로 사용
function navigate(route: Route) {
  console.log(`${route}로 이동`);
}

navigate('/about');    // OK
// navigate('/blog');  // ❌ Error

면접에서 "배열에서 유니온 타입을 추출하는 방법"을 물어보면 as const + (typeof arr)[number] 패턴을 설명하면 됩니다.

구조 분해 할당과 타입

TYPESCRIPT
// 튜플 구조 분해
const [first, second]: [string, number] = ['hello', 42];

// 배열 구조 분해 — 타입은 추론됨
const [head, ...rest] = [1, 2, 3, 4]; // head: number, rest: number[]

// 튜플의 구조 분해 — 정확한 타입
const tuple = [1, 'hello', true] as const;
const [a, b, c] = tuple; // a: 1, b: 'hello', c: true

배열 메서드와 타입 추론

TYPESCRIPT
const numbers = [1, 2, 3, 4, 5];

// map — 반환 타입이 자동 추론됨
const doubled = numbers.map((n) => n * 2); // number[]
const strings = numbers.map((n) => String(n)); // string[]

// filter — 타입 가드를 쓰면 타입이 좁혀짐
const mixed: (string | number)[] = [1, 'hello', 2, 'world'];

// 일반 filter — (string | number)[]
const filtered = mixed.filter((item) => typeof item === 'string');

// 타입 가드 filter — string[]
const onlyStrings = mixed.filter(
  (item): item is string => typeof item === 'string'
);

정리

  • readonly 배열/튜플은 수정 메서드를 차단한다
  • 튜플 요소에 이름을 붙이면 가독성이 좋아진다 (Labeled Tuples)
  • Rest Element로 가변 길이 튜플을 정의할 수 있다
  • as const는 배열을 읽기 전용 리터럴 튜플로 변환한다
  • (typeof arr)[number]로 배열에서 유니온 타입을 추출할 수 있다
  • filter에 타입 가드를 쓰면 반환 타입이 좁혀진다
댓글 로딩 중...