Menu

TypeScript JSON: JSON.parse 타입, JSON을 인터페이스로, import

JSON.parse는 any를 반환하므로 TypeScript는 결과에 붙인 타입을 그대로 믿습니다. 파싱한 JSON에 타입을 지정하는 방법, 타입 가드로 검증하기, JSON 샘플을 인터페이스로 바꾸기, .json 파일 import하기, JSON.stringify가 Date, Set, undefined를 어떻게 처리하는지 알아봅니다.

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

JSON.parse는 any를 반환하므로 TypeScript는 결과를 어떤 타입에 대입하든 받아들입니다. 덕분에 파싱한 JSON에 타입을 붙이는 것은 한 줄이면 되지만, 그 타입은 컴파일러가 검사하는 것이 아니라 여러분이 하는 약속이라는 뜻이기도 합니다.

두 번째 객체의 age는 문자열 "41"입니다. any는 무엇에든 대입할 수 있으므로 TypeScript는 여전히 이를 number라고 부르고, 프로그램은 411을 출력합니다. 방금 자신의 코드가 쓴 JSON이라면 타입 표기로 충분합니다. 요청, 파일, 로컬 스토리지에서 온 데이터라면 검증하세요.

unknown으로 파싱하기

결과의 타입을 unknown으로 지정하면 컴파일러는 프로퍼티를 쓰기 전에 검사를 요구합니다.

오류는 index.ts(4,13): error TS18046: 'data' is of type 'unknown'.입니다. 이후 작성하는 검사 하나하나가 data를 조금씩 더 좁힙니다.

타입 가드로 검증하기

타입 가드는 value is User를 반환하는 함수입니다. 이 함수가 true를 반환하면 TypeScript는 그 뒤로 값을 User로 취급하며, 함수 안의 검사는 실제 런타임 검사입니다.

JSON.parse 자체도 잘못된 형식의 텍스트에 SyntaxError를 던지므로 실제 코드에서는 try/catch로도 감쌉니다. 크거나 중첩된 데이터라면 직접 쓴 가드가 길어집니다. Zod나 Valibot 같은 스키마 라이브러리를 쓰면 형태를 한 번만 선언하고 거기서 검증기와 TypeScript 타입을 모두 얻을 수 있습니다.

JSON을 TypeScript 인터페이스로

JSON 샘플을 타입으로 바꾸는 일은 기계적입니다. 다음 응답이 있다고 합시다.

{
    "id": 42,
    "title": "Learn TypeScript",
    "done": false,
    "owner": { "id": 7, "name": "Ada" },
    "tags": ["study", "ts"],
    "dueDate": "2024-03-15T10:30:00Z",
    "notes": null
}

각 값을 타입으로 옮기고, 중첩 객체에는 별도의 인터페이스를 주고, 달라질 수 있는 부분을 표시합니다.

샘플 하나로는 어떤 필드가 선택적이거나 null이 될 수 있는지 알 수 없습니다. ?와 | null을 정하기 전에 여러 응답이나 API 문서를 살펴보세요.

날짜와 reviver

JSON에는 날짜 타입이 없으므로 날짜는 문자열로 도착합니다. JSON.parse의 두 번째 인수인 reviver는 키마다 호출되며 날짜를 다시 만들 수 있습니다.

reviver의 value 매개변수도, 결과도 any이므로 Order 타입은 여전히 검사되는 것이 아니라 믿어지는 것입니다. JSON.stringify(order, null, 2)는 출력을 공백 두 칸으로 들여 쓰고 Date를 다시 ISO 문자열로 바꿉니다.

JSON.stringify와 잃어버리는 것

JSON.stringify는 string을 반환하도록 타입이 지정되어 있습니다. 변환한 값이 항상 그대로 돌아오지는 않으며, 타입은 이를 경고하지 않습니다.

값JSON.stringify 후
DateISO 문자열(toJSON 메서드를 통해)
Map, Set{}(먼저 [...set]이나 Object.fromEntries(map)으로 변환)
객체 안의 undefined, 함수, 심볼키가 빠짐
배열 안의 undefined, 함수, 심볼null
단독으로 쓴 undefined, 함수, 심볼문자열이 아니라 undefined
NaN, Infinitynull
bigintTypeError를 던짐

런타임 규칙은 순수 JavaScript와 같으며, JavaScript의 JSON에서 다룹니다.

모든 JSON 값을 위한 타입

임의의 JSON을 다루는 코드라면, 재귀 타입으로 JSON이 담을 수 있는 것을 정확히 기술하고 담을 수 없는 값을 거부할 수 있습니다.

주석이 없으면 마지막 줄은 Date 객체가 JsonValue가 아니므로 컴파일 오류입니다.

.json 파일 import하기

.json 파일은 모듈처럼 import할 수 있으며, TypeScript는 내용에서 타입을 추론합니다.

{ "name": "app", "port": 8080, "tags": ["a"] }
// CommonJS output, or a bundler
import config from "./config.json";
const port: number = config.port; // typed from the file: number

// An ES module under module: nodenext
import settings from "./config.json" with { type: "json" };

TypeScript 7에서는 module이 nodenext, node20, commonjs, esnext, preserve라면 추가 설정 없이 동작합니다. node16과 node18에서는 "resolveJsonModule": true를 추가하기 전까지 TS2732 오류 Cannot find module './config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.로 실패하며, 이 옵션을 false로 설정하면 모든 곳에서 JSON import가 꺼집니다. nodenext나 node20의 ES 모듈에서는 import에 with { type: "json" } 속성이 필요하고(없으면 TS1543 오류), default import만 허용됩니다(import { port }는 TS1544 오류). tsc는 import한 .json 파일을 컴파일된 JavaScript와 함께 outDir로 복사합니다.

자주 묻는 질문

TypeScript에서 JSON.parse는 어떤 타입을 반환하나요?

any입니다. 컴파일러는 문자열에 무엇이 들어 있는지 알 수 없으므로, const user: User = JSON.parse(text)는 텍스트 내용과 관계없이 컴파일됩니다. 데이터가 프로그램 외부에서 온다면 결과를 unknown에 대입하고 검증하세요.

JSON을 TypeScript 인터페이스로 변환하려면 어떻게 하나요?

대표적인 샘플을 골라 키마다 프로퍼티를 하나씩 씁니다. 문자열, 숫자, boolean은 string, number, boolean이 되고, 중첩 객체는 별도의 인터페이스가, 객체 배열은 Item[]이 되며, 가끔 빠지는 키에는 ?를 붙입니다. quicktype 같은 코드 생성기가 이를 자동화하지만, 생성된 추측은 여러 샘플과 대조해 확인하세요.

TypeScript에서 JSON 파일은 어떻게 import하나요?

TypeScript 7에서 module이 nodenext, node20, commonjs, esnext, preserve라면 import config from "./config.json";이 동작하며, 결과는 파일 내용에서 타입을 받습니다. node16이나 node18에서는 "resolveJsonModule": true도 설정하세요. nodenext나 node20의 ES 모듈에서는 Node가 요구하는 속성을 추가합니다: import config from "./config.json" with { type: "json" };.

JSON.stringify는 항상 문자열을 반환하나요?

타입은 string이라고 하지만, JSON.stringify(undefined)와 JSON.stringify(() => 1)은 실행 시점에 undefined를 반환합니다. 객체 안의 값도 변환됩니다. Date는 ISO 문자열이 되고, Map과 Set은 {}가 됩니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기