Menu

TypeScript 객체 타입: 선택적 프로퍼티와 readonly

TypeScript에서 객체에 타입을 지정하는 방법: 인라인 객체 타입, ?를 쓴 선택적 프로퍼티, readonly 프로퍼티, 중첩 객체, 메서드, 초과 프로퍼티 검사, 그리고 object, {}, Object의 차이를 알아봅니다.

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

TypeScript 객체 타입은 객체가 가진 프로퍼티와 각 프로퍼티의 타입을 나열합니다: { name: string; age: number }. ?를 붙이면 선택적 프로퍼티가 되고, readonly를 붙이면 다시 대입할 수 없게 됩니다. 타입을 인라인으로 쓰거나, type이나 interface로 이름을 붙여 재사용하세요.

객체 타입 작성하기

프로퍼티는 ;나 ,로 구분하며(둘 다 되고, 보통 ;를 씁니다), 줄바꿈만으로도 충분합니다. 한 번만 쓰는 매개변수라면 인라인 타입으로 괜찮지만, 두 번 이상 쓰는 것에는 이름을 붙이세요.

// Inline, in a parameter
function area(rect: { width: number; height: number }): number {
    return rect.width * rect.height;
}

// Named with a type alias
type Rect = { width: number; height: number };

// Named with an interface (the same shape)
interface RectShape {
    width: number;
    height: number;
}

type과 interface는 객체 형태를 똑같이 잘 기술합니다. 차이점(선언 병합, 유니언)은 interface와 type 비교 페이지에서 다룹니다.

타입에 선언되지 않은 프로퍼티에 접근하면 컴파일 오류입니다. point.z는 TS2339 Property 'z' does not exist on type '{ x: number; y: number; }'.를 냅니다.

선택적 프로퍼티(Optional Properties)

이름 뒤에 ?를 붙이면 프로퍼티를 생략할 수 있습니다. 선택적 프로퍼티를 읽으면 T | undefined가 되므로, TypeScript는 사용하기 전에 값이 없는 경우를 처리하게 합니다.

검사 없이 선택적 프로퍼티의 메서드를 호출하면 오류입니다. p.nickname.toUpperCase()는 TS18048 'p.nickname' is possibly 'undefined'.로 실패합니다. undefined가 결과로 괜찮다면 옵셔널 체이닝(p.nickname?.toUpperCase())을 쓰세요.

prop?: T와 prop: T | undefined는 같지 않습니다. 앞의 것은 키가 없어도 되고, 뒤의 것은 값이 undefined여도 되지만 키가 반드시 있어야 합니다.

readonly 프로퍼티

readonly는 객체를 만든 뒤 프로퍼티를 다시 대입하지 못하게 합니다. 컴파일 시점 검사일 뿐이며 얕게(shallow) 적용됩니다. readonly 프로퍼티에 저장된 객체나 배열의 내부는 여전히 바꿀 수 있습니다.

출력이 두 한계를 모두 보여 줍니다. id는 런타임에 실제로 바뀌었고(readonly라는 사실은 컴파일러만 알았습니다), 내부 배열도 수정됐습니다. readonly 배열에는 readonly string[]를 쓰고, 모든 프로퍼티를 한 번에 readonly로 만들려면 Readonly<Order>를 쓰세요.

초과 프로퍼티 검사(Excess Property Checks)

객체 리터럴을 타입이 지정된 변수에 바로 대입하거나 타입이 지정된 매개변수에 바로 넘기면, TypeScript는 타입에 선언되지 않은 프로퍼티를 거부합니다. 새로 만든 리터럴의 추가 프로퍼티는 거의 항상 오타이기 때문입니다.

index.ts(8,8): error TS2561: Object literal may only specify known properties, but 'colour' does not exist in type 'Options'. Did you mean to write 'color'?

컴파일러가 비슷한 이름을 찾았기 때문에 코드가 TS2561입니다. 비슷한 이름이 없는 추가 프로퍼티라면 TS2353 Object literal may only specify known properties, and 'z' does not exist in type 'Point'.가 납니다. 이 검사가 없다면 오타가 그대로 컴파일되고, color는 undefined가 되며, 프로그램은 아무 말 없이 검은색으로 그립니다. 이 검사는 새로 만든 리터럴에만 적용됩니다. 이미 변수에 담긴 객체는 추가 프로퍼티를 가져도 됩니다. TypeScript의 타이핑은 구조적이어서, 필요한 프로퍼티를 최소한 가지고 있으면 값이 타입에 맞기 때문입니다.

중첩 객체와 메서드

객체 타입은 중첩할 수 있으며, 메서드 문법이나 함수 타입 프로퍼티로 메서드를 기술할 수 있습니다.

깊거나 재사용하는 형태라면 내부 타입에 이름을 붙여(type Address = { ... }) 참조하거나, 마지막 줄처럼 인덱스 접근 Company["address"]로 바깥 타입에서 꺼내 쓰세요.

object, {}, Object 비교

비슷하게 들리지만 뜻이 다른 세 가지 타입입니다.

타입받는 값거부하는 값
object원시 값이 아닌 모든 것: {}, [], 함수, 클래스 인스턴스5, "a", true, null, undefined
{}null과 undefined를 제외한 모든 값(원시 값 포함)null, undefined
Object{}와 같으며, toString 같은 내장 멤버의 타입이 호환되는지도 검사null, undefined
{ x: number }숫자 x를 가진 모든 값x가 없는 값

{}는 "빈 객체"가 아니라 "null도 undefined도 아님"을 뜻합니다. 키를 알 수 없는 아무 객체나 받으려면 Record<string, unknown>을 쓰고, 키와 값의 맵이라면 딕셔너리 페이지에서 설명하는 인덱스 시그니처나 Record를 쓰세요. 대부분의 경우 이 셋보다는 구체적인 형태가 낫습니다.

자주 묻는 질문

TypeScript에서 객체 타입은 어떻게 정의하나요?

중괄호 안에 프로퍼티와 그 타입을 나열합니다: { name: string; age: number }. 타입 표기에 인라인으로 쓰거나, type User = { ... } 또는 interface User { ... }로 이름을 붙여 재사용할 수 있습니다. 프로퍼티는 ;나 ,로 구분합니다.

TypeScript에서 프로퍼티를 선택적으로 만들려면 어떻게 하나요?

프로퍼티 이름 뒤에 ?를 붙입니다: { name: string; nickname?: string }. 객체에서 nickname을 생략할 수 있고, 읽으면 string | undefined가 되므로 문자열로 쓰기 전에 검사하거나 기본값(user.nickname ?? user.name)을 주어야 합니다.

prop?: string과 prop: string | undefined의 차이는 무엇인가요?

prop?: string은 키 자체를 생략할 수 있습니다. prop: string | undefined는 값이 undefined여도 되지만 키는 필수이므로 {}는 컴파일 오류입니다. 어느 쪽이든 읽으면 string | undefined입니다.

TypeScript에서 object, {}, Object의 차이는 무엇인가요?

object는 원시 값이 아닌 모든 값(객체, 배열, 함수)을 뜻하며 5나 "a"를 거부합니다. {}는 null과 undefined를 제외한 모든 값을 뜻하며 원시 값도 포함합니다. Object는 {}와 거의 같지만 toString 같은 내장 메서드의 타입이 호환되는지도 검사합니다. {}나 Object보다는 object를, 더 좋게는 구체적인 형태를 쓰세요.

객체 리터럴은 알려진 프로퍼티만 지정할 수 있다는 오류는 왜 나나요?

초과 프로퍼티 검사(excess property check)입니다. TS2353 오류이며, 의도했을 법한 프로퍼티를 컴파일러가 제안할 수 있으면 TS2561입니다. 새로 만든 객체 리터럴을 타입이 지정된 변수나 매개변수에 바로 대입하면 타입에 선언되지 않은 프로퍼티는 대개 오타이므로 표시됩니다. 다른 변수에 저장된 객체를 대입하면 이 검사를 건너뜁니다. 구조적 타이핑에서는 추가 프로퍼티가 허용되기 때문입니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기