객체의 속성명(key)을 타입으로 사용하고싶을때...
const obj = { a: "123", b: "hello", c: "world" }
type Key = keyof obj // obj는 "값"이기 때문에 타입으로 타이핑할 수 없기 때문에 오류로 표기된다.
const obj = { a: "123", b: "hello", c: "world" }
type Key = keyof typeof obj // obj의 키(key)들을 타입으로써 사용가능하다. type Key = "a" | "b" | "c" 와 같다.
// USE CASES...
// 1
type Movie = {
id: number;
name: string;
lengthInMinutes: number;
}
const titanic: Movie = {
id: 1,
name: 'Titanic',
lengthInMinutes: 269
}
type GetPropertyType<T> = (obj: T, key: keyof T) => T[keyof T]
const getProperty: GetPropertyType<Movie> = (obj, key) => obj[key]
getProperty(titanic, 'name') // Titanic
getProperty(titanic, 'lengthInMinutes') // 269
getProperty(titanic, 'description') // not assignable error
// 2
type Optional<Type> = {
[Property in keyof Type]+?: Type[Property]
}
type Movie = {
name: string
lengthInMinutes: number
description: string
}
type MutableMovie = Optional<Movie>
type MutableMovie = Partial<Movie>
반대로 값(value)을 타입으로 사용하는 방법은
const statuses = {
pending: "PENDING",
inProgress: "IN_PROGRESS",
completed: "COMPLETED"
} as const;
type Status = typeof statuses[keyof typeof statuses]; // Type: "PENDING" | "IN_PROGRESS" | "COMPLETED"
여기서 "as const" 가 중요한데
const status = {
pending: "PENDING",
inProgress: "IN_PROGRESS",
completed: "COMPLETED"
} as const
statuses.pending = 'STANDBY'; // error: Cannot assign to 'pending' because it is a read-only property
// 아래처럼 되었다고 보면 된다. (immutable)
const status = {
readonly pending: "PENDING",
readonly inProgress: "IN_PROGRESS",
readonly completed: "COMPLETED"
} as const
객체에 "as const" 를 붙혀주면 그 객체는 "readonly" 의 상태가되어 immutable 된다. 즉, 해당 객체에 있는 프로퍼티에 값을 재할당할 수 없다.
"as const" 를 사용한다는 것은 해당 변수의 값이 개발자의 의도가 담긴 어떠한 규칙이 있다는 것을 의미이다.
// Type
type Status = {
pending: "PENDING"
inProgress: "IN_PROGRESS"
completed: "COMPLETED"
}
// Object value
const status = {
pending: "PENDING",
inProgress: "IN_PROGRESS",
completed: "COMPLETED"
} as const
하나는 타입을 정의한 것이고, 또 하나는 메모리에 저장될 객체, 즉 값이다.
위 두개는 같아보이지만 미세한 차이가 있다.
// Using "as const"
const status = {
pending: "PENDING",
inProgress: "IN_PROGRESS",
completed: "COMPLETED"
} as const
type StatusTypeA = typeof status[keyof typeof status]; // "PENDING" | "IN_PROGRESS" | "COMPLETED"
type StatusKeyA = keyof typeof status; // "pending" | "inProgress", "completed", 객체 리터럴의 속성명을 타입으로 만들때 as const 가 없어도된다.
// Two seperate types
type Status = {
pending: "PENDING"
inProgress: "IN_PROGRESS"
completed: "COMPLETED"
}
type StatusTypeB = Status[keyof Status]; // "PENDING" | "IN_PROGRESS" | "COMPLETED"
type StatusKeyB = keyof Status; // keyof Status
StatusTypeB를 VSC에서 hover 했을때 "keyof Status" 로 표기되는 것을 볼 수 있다. 나는 "keyof Status" 가 아닌, 정확히 어떤 값("pending" | "inProgress" | "completed")인지 보여줬으면 했다.
왜 이렇게 표현 방식이 다른지 알아보았는데, 타입스크립가 컨텍스트에 따라 타입을 추론하는 방식이 다르기 때문이다.
타입스크립트는 StatusTypeA 에 타입을 정의할때 객체 리터럴의 immutable/readonly 된 값에서 추론한 반면, StatusTypeB 는 타입 자체에서 추론했다고 볼 수 있다.
즉, 타입스크립트는 객체 리터럴을 구체적으로 보고 추론한 반면 후자는 제너럴하게 추론한 것이다.
type AlphabetTypeA = string[]; // 제너럴한 추론
type AlphabetTypeB = ['abc', 'defg', 'hijk']; // 구체적인 추론
const array:AlphabetTypeA = ['abc', 'defg', 'hijk'];
const array:AlphabetTypeB = ['abc', 'defg', 'hijk'];
array 는 AlphabetTypeA 와 AlphabetTypeB 를 모두 만족한다.
'TypeScript' 카테고리의 다른 글
[패턴] Discriminating unions - To narrow down the type within a union type = 타입 가드 (0) | 2024.06.05 |
---|---|
제네릭 Generic (0) | 2024.06.05 |
useEffect 안에서 useRef 사용 시 타이핑 (0) | 2022.09.24 |
[객체타입] readonly, 인덱스드 시그니처, 맵드 타입스 (0) | 2022.09.15 |
Type guard / narrowing / predicates (0) | 2022.09.13 |
댓글