Menu

TypeScript 반복문: for, forEach, for...of, for...in

TypeScript의 모든 반복 방법과 각각이 주는 타입: 기본 for 문, 배열과 Map, 문자열을 도는 for...of, 문자열 키를 주는 for...in, forEach(break도 await도 안 됨), 객체 키를 타입 안전하게 순회하기, while.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

TypeScript의 반복문은 JavaScript와 같습니다: for, for...of, for...in, while, do...while, 그리고 배열 메서드 forEach. TypeScript가 더하는 것은 타입입니다. for...of의 반복 변수는 요소 타입을 가지고, for...in의 키는 항상 string입니다.

대부분의 배열 반복에는 for...of가 가장 알맞습니다. 읽기 쉽고, break, continue, await를 지원하며, 타입 표기가 필요 없습니다.

기본 for 문

for (init; condition; update)는 횟수를 세거나, 한 번에 여러 칸씩 건너뛰거나, 거꾸로 도는 반복에 씁니다. TypeScript는 카운터의 타입을 초기값에서 추론합니다.

카운터는 var가 아니라 let으로 선언하세요. let을 쓰면 반복마다 새로운 바인딩이 생기므로, 반복문 안에서 만든 콜백이 그 회차의 값을 봅니다.

for...of: 배열, 문자열, Map의 값

for...of는 이터러블이면 무엇에든 동작하며, 반복 변수는 그에 맞는 타입을 받습니다. 인덱스도 필요하면 [index, value] 튜플을 내놓는 array.entries()를 순회하세요.

break는 반복을 빠져나가고 continue는 다음 항목으로 넘어가며, for 문과 똑같이 동작합니다. 런타임 세부 사항(이터러블, 제너레이터)은 JavaScript for...of와 for...in 페이지에 있습니다.

forEach: break도 await도 안 된다

forEach는 요소마다 함수를 호출하고 undefined를 반환합니다. 콜백의 매개변수는 배열에서 타입을 받으므로 타입 표기가 필요 없습니다. 다만 할 수 없는 일이 두 가지 있습니다.

  • 일찍 멈출 수 없습니다. 콜백 안의 break는 컴파일 오류 TS1107 Jump target cannot cross function boundary.입니다. return은 현재 호출만 끝내므로 continue처럼 동작합니다.
  • 비동기 작업을 기다리지 않습니다. forEach는 async 콜백이 반환하는 Promise를 무시합니다.

비동기 함정은 경고 없이 컴파일되므로 놓치기 쉽습니다.

forEach 콜백들은 반복이 이미 다음으로 넘어간 뒤에 타이머 순서(10, 20, 30)대로 끝납니다. for...of 버전은 차례대로 실행합니다. 병렬로 실행하면서도 끝날 때까지 기다리려면 await Promise.all(items.map(async (item) => { ... }))를 쓰세요.

for...in: 키는 문자열이다

for...in은 객체의 열거 가능한 프로퍼티 이름을 순회합니다. 키의 타입은 항상 string이며, 배열에서도 키는 문자열로 된 인덱스입니다. 그래서 타입이 지정된 객체를 인덱싱하기에는 불편합니다.

index.ts(5,22): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; age: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ name: string; age: number; }'.

TypeScript가 키를 string으로 지정하는 것은 의도된 것입니다. 런타임에 객체는 타입에 나열된 것보다 많은 프로퍼티를 가질 수 있고(구조적 타이핑은 추가 프로퍼티를 허용합니다), for...in은 상속된 열거 가능한 프로퍼티도 순회합니다. 그래서 키가 "name" | "age"라고 보장할 수 없습니다. 해결 방법은 다음 절에 있습니다. 배열에는 for...in을 아예 쓰지 마세요.

객체의 키와 값 순회하기

Object.entries는 [key, value] 쌍을 주며, 객체를 순회하는 일반적인 방법입니다. Object.keys도 for...in과 같은 이유로 string[]를 반환하므로, 객체에 추가 키가 없다는 것을 안다면 키 타입을 단언하세요.

키를 미리 알 수 없는 데이터라면 객체를 Record<string, number>로 타입 지정하거나 Map을 쓰세요. 둘 다 딕셔너리 페이지에서 다룹니다.

while과 do...while

while은 매번 실행하기 전에 조건을 검사하고, do...while은 본문을 한 번 실행한 뒤에 검사합니다. 조건이 만족될 때까지 읽는 것처럼 컬렉션을 순회하지 않는 반복에 알맞습니다. 좁히기(narrowing)도 동작합니다. 조건에서 !== undefined를 검사하면 본문에서는 변수가 좁혀집니다.

중첩 반복문에서 레이블, break, continue

일반 break는 가장 안쪽 반복만 빠져나갑니다. 바깥 반복을 빠져나가려면 레이블을 붙이고 그 레이블을 지정하세요.

어떤 반복문을 쓸까

목적반복문
배열의 모든 요소for (const x of arr)
요소와 인덱스for (const [i, x] of arr.entries()) 또는 arr.forEach((x, i) => ...)
일찍 멈추기break와 함께 for...of, 또는 find, some, every
단계마다 await안에서 await를 쓰는 for...of
새 배열 만들기map, filter(반복문이 아님)
횟수 세기, n씩 건너뛰기for (let i = 0; ...; i += n)
객체의 키와 값for (const [k, v] of Object.entries(obj))
Map 항목for (const [k, v] of map)
조건이 만족될 때까지while

자주 묻는 질문

TypeScript에서 for 문은 어떻게 쓰나요?

JavaScript와 같습니다: for (let i = 0; i < 5; i++) { ... }. TypeScript는 i를 number로 추론합니다. 배열의 요소를 차례로 돌려면 for (const item of items)가 더 짧고, item은 배열의 요소 타입을 받습니다.

TypeScript에서 forEach를 break로 빠져나오려면 어떻게 하나요?

할 수 없습니다. 콜백 안의 break는 컴파일 오류 TS1107(Jump target cannot cross function boundary)이고, return은 현재 콜백 호출만 끝내므로 continue처럼 동작합니다. break와 함께 for...of를 쓰거나, 설계상 일찍 멈추는 some/find/every를 쓰세요.

TypeScript에서 for...of와 for...in의 차이는 무엇인가요?

for...of는 이터러블의 값(배열 요소, 문자열의 글자, Map 항목)을 실제 타입으로 방문합니다. for...in은 객체의 열거 가능한 프로퍼티 키를 방문하며, 키는 항상 string 타입이고 배열 인덱스도 문자열로 나옵니다. 배열에는 for...of를, 객체에는 Object.entries를 쓰세요.

TypeScript에서 객체의 키와 값을 순회하려면 어떻게 하나요?

for (const [key, value] of Object.entries(obj))를 씁니다. key는 string이고 value는 프로퍼티 타입들의 유니언입니다. key를 keyof typeof obj 타입으로 써야 한다면 단언하세요: (Object.keys(obj) as (keyof typeof obj)[]). TypeScript는 의도적으로 Object.keys를 string[]로 타입 지정합니다.

forEach 안에서 await를 쓸 수 있나요?

컴파일은 되지만 forEach는 콜백이 반환하는 Promise를 기다리지 않으므로, 작업이 끝나기 전에 반복이 끝납니다. 순차적으로 처리하려면 안에서 await를 쓰는 for...of를, 병렬로 처리하려면 await Promise.all(items.map(async (x) => ...))를 쓰세요.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기