본문 바로가기
C.W.K.
Stream
Lesson 02 of 05 · published

튜플: 각 위치가 자기 역할을 가질 때

~10 min · arrays-tuples, tuples, positional-types

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"튜플은 길이와 순서가 계약인 배열이야. 위치가 이름처럼 읽힐 때만 써."

고정 길이와 위치별 타입

[string, number]는 정확히 두 칸인 튜플이야. 첫 칸은 문자열, 둘째 칸은 숫자라고 각 위치의 타입을 따로 추적해. 일반 배열처럼 원소가 몇 개든 들어가는 모양이 아니므로 빠뜨리거나 하나 더 넣으면 오류가 난다.

좌표 [x, y], 키와 값의 쌍, React의 [state, setState]처럼 순서가 널리 알려져 있고 구조 분해해서 곧바로 쓰는 값에 잘 맞아. 두세 칸을 넘어서 역할을 외워야 한다면 객체가 더 낫다.

선택 칸과 나머지 칸

[string, number?]처럼 마지막 칸을 선택 사항으로 둘 수 있고, [string, ...number[]]처럼 첫 칸 뒤에 같은 타입이 몇 개든 이어지게 할 수 있어. 함수 매개변수 목록과 닮은 규칙이 적용돼.

이름 있는 튜플 원소 [start: number, end: number]는 실행값을 바꾸지 않지만 편집기의 도움말에 역할을 보여 줘. 위치를 계약으로 선택했다면 최소한 이름으로 의미를 남겨 주는 게 좋아.

튜플과 객체의 선택

구조 분해한 변수 이름만으로 뜻이 곧바로 보이고 값의 수가 작다면 튜플이 간결해. 필드가 늘어날 가능성이 있거나 일부 값만 골라 쓰거나, 호출자마다 순서를 기억해야 한다면 객체가 이겨. 튜플 끝에 새 칸을 더하면 모든 구조 분해 자리가 영향을 받지만 객체는 이름으로 접근하므로 확장에 더 여유가 있어.

추론은 기본적으로 배열을 고른다

[1, 'two']만 적으면 TypeScript는 보통 숫자와 문자열이 섞인 배열로 추론해. 튜플 의도를 알려면 const pair: [number, string]처럼 표기하거나 as const로 고정해야 해. 후자는 읽기 전용 리터럴 튜플이 된다는 차이가 있어.

위치가 계약이면 튜플, 이름이 계약이면 객체. 짧다는 이유만으로 객체를 튜플로 압축하면 절약한 글자보다 독자가 외워야 할 순서가 더 비싸져.

피파의 고백

세 칸짜리 튜플까지는 익숙한 문맥이면 괜찮아. 네 칸째부터는 구조 분해한 변수 이름을 매번 확인하게 되더라. 그 순간 이미 객체가 답이야. 타입은 기계만 읽는 압축 포맷이 아니니까.

Code

Modifier 다 가진 tuple·typescript
// Tuple — 고정 길이, 위치별 타입.

type Coord = [number, number];
const origin: Coord = [0, 0];
const point: Coord = [3, 4];
// const bad: Coord = [3];          // ❌ Element 빠짐
// const bad2: Coord = [3, 4, 5];   // ❌ Element 너무 많음

// Optional 과 rest element.
type OptionalSecond = [string, number?];
const x1: OptionalSecond = ['hi'];
const x2: OptionalSecond = ['hi', 42];

type Spread = [string, ...number[]];
const y: Spread = ['header', 1, 2, 3, 4];

// Named tuple element — 가독성용 label.
function getRange(): [start: number, end: number] {
  return [0, 100];
}
const [s, e] = getRange();  // s 와 e 명확성 위해 이름 붙음
Tuple 을 contract 로 — useState 와 Go-style 에러 반환·typescript
// useState 패턴 — 위치 contract.

type StateHook<T> = [T, (next: T) => void];

function useCounter(initial: number): StateHook<number> {
  let value = initial;
  const set = (next: number) => { value = next };
  return [value, set];
}

const [count, setCount] = useCounter(0);
setCount(count + 1);

// Return-with-status 패턴.
function parse(input: string): [error: string | null, result: number | null] {
  const n = Number(input);
  if (Number.isNaN(n)) return ['not a number', null];
  return [null, n];
}

const [err, result] = parse('42');
if (err === null) console.log(result);

External links

Exercise

분자와 분모를 받아 몫과 나머지를 이름 있는 튜플로 반환하는 divmod를 작성해. 객체 반환 버전과 호출 코드를 비교해 어느 쪽을 출시할지 판단해.
Hint
몫과 나머지처럼 위치 의미가 안정된 두 값은 이름 있는 튜플이 편해. 반환 항목이 셋 이상이거나 자주 늘어난다면 객체가 대개 더 읽기 좋아.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.