Menu

TypeScript Map 사용법: Map<K, V>, get, set, has, 순회

TypeScript에서 Map 사용하기: 타입이 지정된 Map<K, V> 만들기, get이 V | undefined를 반환하는 이유, set, has, delete, 삽입 순서대로 순회하기, 객체 키, 객체와 JSON으로 변환하기, Map과 객체와 Record 비교, array.map()의 타입.

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

TypeScript의 Map은 키와 값에 타입이 붙은 JavaScript 내장 Map입니다. Map<string, number>는 문자열을 숫자에 매핑합니다. new Map<K, V>()로 만든 뒤 set, get, has, delete를 씁니다. 키가 없을 수 있으므로 get은 V | undefined를 반환합니다.

TypeScript에서 Map을 유용하게 만드는 것은 타입 인수입니다. 모든 set이 검사되고 모든 get이 값 타입을 반환합니다. 마지막 줄은 컴파일 오류(TS2345)이며, @ts-expect-error 덕분에 블록이 계속 실행됩니다.

이 페이지는 Map 컬렉션을 다룹니다. array.map()을 찾아왔다면 마지막 절을 보세요.

Map 만들기

타입은 타입 인수, 초기 항목, 또는 타입 표기에서 옵니다. 초기 항목은 [key, value] 튜플의 배열이거나, 그런 튜플을 내놓는 다른 무엇이든 됩니다.

추론에는 함정이 두 가지 있습니다.

  • 타입 인수도 항목도 없는 new Map()은 Map<any, any>입니다. 넣고 꺼내는 어떤 것도 검사되지 않습니다. 항상 new Map<K, V>()로 쓰세요.
  • 값 타입이 서로 다른 항목에서는 유니언이 추론되지 않습니다. new Map([["a", 1], ["b", "x"]])는 TS2769 오류(No overload matches this call)입니다. 타입을 직접 쓰세요: new Map<string, number | string>([...]).

get은 V | undefined를 반환한다

Map은 키가 존재한다고 보장할 수 없으므로 get의 타입은 V | undefined입니다. strict에서는 값을 V로 쓰기 전에 undefined를 처리해야 합니다.

index.ts(4,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
  Type 'undefined' is not assignable to type 'number'.

해결 방법을 흔히 쓰는 순서대로 보면 다음과 같습니다.

TypeScript는 나중에 get을 호출할 때 has가 true를 반환했다는 것을 기억하지 않습니다. get의 결과를 직접 검사하는 편이 더 짧고 안전합니다. non-null 단언 stock.get("apples")!는 오류를 없애지만 키가 없을 때 아무런 보호도 해 주지 않습니다.

Map 메서드와 타입

멤버Map<K, V>에서의 타입설명
new Map<K, V>(entries?)Map<K, V>entries: [K, V]의 이터러블
set(key, value)this추가하거나 교체, 체이닝 가능
get(key)V | undefined없으면 undefined
has(key)boolean
delete(key)boolean무언가 삭제됐으면 true
clear()void모두 삭제
sizenumber메서드가 아니라 프로퍼티
keys(), values()K, V의 이터레이터배열로 스프레드: [...map.keys()]
entries(), for...of[K, V]의 이터레이터삽입 순서
forEach((value, key) => ...)void주의: 값이 먼저 옵니다

Map 순회하기

Map은 삽입 순서대로 순회합니다. 맵을 for...of로 돌면 [K, V] 타입의 [key, value] 튜플이 나옵니다.

이미 있는 키에 set하면 값은 바뀌지만 순서상의 원래 위치는 유지됩니다.

객체 키와 개수 세기

객체와 배열을 포함해 어떤 값이든 키가 될 수 있습니다. 키는 ===처럼 비교되므로 내용이 같은 두 객체도 서로 다른 키입니다. Map은 항목의 개수를 세거나 그룹으로 묶는 표준적인 방법이기도 합니다.

객체의 내용을 기준으로 키를 삼으려면 user.id나 `${x},${y}`처럼 문자열이나 숫자 키를 따로 만드세요.

Map과 객체와 Record 비교

Map<K, V>객체 / Record<string, V>
키 타입무엇이든, ===처럼 비교string(숫자는 문자열이 됨), symbol
없는 키의 타입get이 V | undefined 반환noUncheckedIndexedAccess가 꺼져 있으면 obj[key]는 V
순서삽입 순서대체로 삽입 순서지만, 정수처럼 보이는 키가 오름차순으로 먼저 옴
크기map.sizeObject.keys(obj).length
잦은 추가와 삭제최적화됨최적화되지 않음
JSON바로 안 됨(JSON.stringify(map)은 "{}")바로 됨
리터럴 문법, 구조 분해아니요예
실수로 상속된 키없음"toString" in {}이 true

경험칙은 이렇습니다. 키가 데이터(사용자 id, 단어, 캐시 항목)이고 런타임에 바뀌는 컬렉션에는 Map을, 알려진 키의 고정된 집합이나 JSON으로 오가는 데이터에는 객체 타입이나 Record를 씁니다. 문자열 키 조회에 쓰는 인덱스 시그니처, Record, Map의 비교는 딕셔너리 페이지에 있습니다.

Map을 객체와 JSON으로 변환하기

Map의 항목은 프로퍼티가 아니므로 JSON.stringify는 이를 보지 못합니다. Object.fromEntries와 Object.entries를 거쳐 변환하세요.

JSON.parse는 any를 반환하므로 as Record<...>로 데이터가 어떤 모양이어야 하는지 밝힙니다. 이는 런타임 검사가 아닙니다. 신뢰할 수 없는 JSON은 그 타입을 믿기 전에 검증하세요.

array.map()의 타입

"typescript map"을 검색하는 사람 중 상당수는 각 요소를 변환해 새 배열을 반환하는 배열 메서드를 찾습니다. 그 타입은 콜백에서 추론되므로 타입 표기가 필요한 경우는 드뭅니다.

콜백의 반환 타입을 표기하는 것((u): Option => ...)이 결과 타입을 밝히는 가장 명확한 방법입니다. 반환하는 객체에서 프로퍼티가 빠지거나 이름이 틀리면 콜백에서 바로 컴파일 오류가 납니다.

자주 묻는 질문

TypeScript에서 Map은 어떻게 만드나요?

생성자에 키와 값의 타입을 넘깁니다: const ages = new Map<string, number>(). 초기 항목이 있으면 타입이 추론됩니다: new Map([["ada", 36]])은 Map<string, number>입니다. 타입도 항목도 없는 new Map()은 Map<any, any>가 되어 검사가 꺼지므로 항상 타입을 지정하세요.

TypeScript에서 Map.get은 왜 undefined를 반환하나요?

키가 없을 수 있으므로 map.get(key)의 타입은 V | undefined입니다. TypeScript는 앞서 호출한 map.has(key)를 나중의 get과 연결하지 않으므로 has 뒤에도 undefined를 처리해야 합니다. 결과를 변수에 담아 검사하거나 ??로 기본값을 주세요.

TypeScript에서 Map과 객체의 차이는 무엇인가요?

Map은 모든 타입(객체 포함)의 키를 받고, 삽입 순서를 유지하며, size가 있고, 잦은 추가와 삭제에 맞게 만들어졌습니다. 일반 객체나 Record<string, V>는 문자열(과 심볼) 키만 가지며, JSON으로 바로 직렬화되고, 리터럴 문법과 구조 분해를 지원합니다. 동적인 키를 가진 컬렉션에는 Map을, 고정된 형태와 JSON 데이터에는 객체를 쓰세요.

TypeScript에서 Map을 객체나 JSON으로 변환하려면 어떻게 하나요?

Object.fromEntries(map)은 Map<string, V>를 일반 객체로 바꾸고, 이를 JSON.stringify로 직렬화할 수 있습니다. Map 자체에 JSON.stringify(map)을 쓰면 "{}"가 나오는데, Map의 항목은 프로퍼티가 아니기 때문입니다. 반대로 변환하려면 new Map(Object.entries(obj))를 씁니다.

TypeScript에서 array.map의 콜백 타입은 어떻게 지정하나요?

대개 지정할 필요가 없습니다. items.map((item) => item.name)은 배열에서 item의 타입을, 콜백이 반환하는 값에서 결과 타입을 추론합니다. 결과 타입을 강제하려면 items.map<string>(...)처럼 타입 인수로 넘기거나 콜백의 반환 타입을 표기하세요.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기