Загружаем каталог…
Загружаем каталог…
본 문서는 Claude를 사용해 정리했습니다. 『실무로 통하는 타입스크립트』 특정 프로퍼티만 선택형으로 만들기 목표 : 일부 프로퍼티만 선택형으로 바꾼 타입을 파생한다. 접근 : 두 객체 타입의 인터섹션 을 만드는 헬퍼 타입을 정의한다. 한 객체는 선택된 키를 선택형( ? )으로 매핑하고, 다른 객체는 나머지 프로퍼티를 그대로 매핑한다. type Person = { name: string; age: number; profession: string; }; // Partial과 달리 지정한 키만 선택형으로 매핑한다 type SelectPartial<T, K extends keyof T> = { [P in K]?: T[P]; }; type Age = SelectPartial<Person, "age">; 나머지 키와 인터섹션 type ExcludeAge = Exclude<"name" | "age", "age">; // "name" type SetOptional<T, K extends keyof T> = { [P in K]?: T[P]; } & { [P in Exclude<keyof T, K>]: T[P]; }; type OptionalAge = SetOptional<Person, "age">; type OptionalAgeAndProf = SetOptional<Person, "age" | "profession">; Exclude<keyof T, K> 로 선택된 키를 뺀 나머지를 구한 뒤, 두 매핑 타입을 & 로 합친다. OptionalAge 에서는 name 과 profession 이 필수, age 만 선택형이다. 내장 헬퍼 타입만으로도 구현할 수 있다 type SetOptional2<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>; type OptionalAge2 = SetOptional2<Person, "age">; 더 짧지만, 결과가 인터섹션 형태라 실제 프로퍼티가 무엇인지 에디터에서 바로 보이지 않고, 어떻게 만들어졌는지만 드러난다. Remap으로 평평하게 만들고, 기본값으로 편의성 높이기 type Remap<T> = { [K in keyof T]: T[K]; }; type SetOptional3<T, K extends keyof T = keyof T> = Remap Partial<Pick<T, K>> & Omit<T, K> >; SetOptional2 SetOptional3 K 기본값 없음 (생략 불가) keyof T K 생략 시 타입 에러 모든 프로퍼티가 선택형 결과 형태 인터섹션 그대로 Remap 으로 하나의 평평한 객체 타입 type AllOptional = SetOptional3<Person>; // 전부 선택형 — {} 도 통과 type OnlyAgeOptional = SetOptional3<Person, "age">; // age만 선택형, name은 여전히 필수 Remap 은 인터섹션을 하나의 객체 타입으로 풀어서, 에디터 툴팁에 실제 프로퍼티가 나열되게 한다. 반대 방향 — SetRequired, OnlyRequired // 일부 키를 꼭 필요하게 type SetRequired<T, K extends keyof T = keyof T> = Remap Required<Pick<T, K>> & Omit<T, K> >; // 제공한 키는 전부 필수, 나머지는 선택형 type OnlyRequired<T, K extends keyof T = keyof T> = Remap Required<Pick<T, K>> & Partial<Omit<T, K>> >; 중첩 객체까지 처리하는 재귀 헬퍼 타입 Partial , Required , Readonly 같은 내장 헬퍼는 객체의 첫 번째 수준만 바꾼다. 중첩된 객체 프로퍼티는 처리하지 못한다. 해결 : 같은 동작을 중첩된 객체에도 반복하는 재귀 헬퍼 타입 을 만든다. type Settings = { mode: "light" | "dark"; playbackSpeed: number; subtitle: { active: boolean; color: string; }; }; const defaults: Settings = { mode: "dark", playbackSpeed: 1.0, subtitle: { active: false, color: "white" }, }; function applySettings( defaultSettings: Settings, userSettings: Partial<Settings>, ): Settings { return { ...defaultSettings, ...userSettings }; } // Partial은 첫 수준에서만 동작한다 — subtitle 안의 color가 빠졌다고 에러 let settings = applySettings(defaults, { subtitle: { active: true } }); TS2741: Property 'color' is missing in type '{ active: true; }' but required in type '{ active: boolean; color: string; }'. DeepPartial type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]>; }; 원시 타입이 T 로 들어와도 에러가 나지 않는다. [K in keyof T]?: 형태의 매핑 타입은 원시 타입을 그대로 통과시켜서, DeepPartial<string> 은 그냥 string 이 된다. // 객체일 때만 깊숙이 들어가도록 조건을 명시한 버전 type DeepPartial2<T> = T extends object ? { [K in keyof T]?: DeepPartial2<T[K]> } : T; type DeepPartialSettings = DeepPartial2<Settings>; 함수 프로퍼티, 배열, string | 객체 유니온, null 네 경우에서 DeepPartial 과 DeepPartial2 의 차이를 재현하지 못했다. 조건 분기는 "원시 타입은 그대로 둔다"는 의도를 코드에 드러내는 표기로 보인다. 스프레드로 합치면 여전히 에러가 난다 function applySettings( defaultSettings: Settings, userSettings: DeepPartial2<Settings>, ): Settings { return { ...defaultSettings, ...userSettings }; // 에러 } Type '{ ...; subtitle: { active?: boolean | undefined; color?: string | undefined; }; }' is not assignable to type 'Settings'. Type 'boolean | undefined' is not assignable to type 'boolean'. defaultSettings 가 Settings 를 만족하니 결과도 괜찮을 것 같지만 그렇지 않다. 스프레드는 얕은 병합이라 subtitle 이 통째로 덮어씌워지기 때문이다. 결과의 subtitle 타입은 userSettings.subtitle 의 타입( active? , color? 가 모두 선택형)이 되어 Settings 의 요구( active: boolean )를 만족하지 못한다. 런타임에서도 같은 문제가 그대로 드러난다. { ...defaults, ...{ subtitle: { active: true } } } // subtitle: { active: true } ← color가 사라짐 lodash의 merge로 해결 기능 구현이 까다로우므로 lodash의 merge 를 이용한다. merge 는 두 객체의 인터섹션을 반환하도록 타입이 정의되어 있다. // @types/lodash merge<TObject, TSource>(object: TObject, source: TSource): TObject & TSource; import { merge } from "lodash"; function applySettings2( defaultSettings: Settings, userSettings: DeepPartial2<Settings>, ): Settings { return merge(defaultSettings, userSettings); } Settings & DeepPartial2<Settings> 는 Settings 에 할당 가능하므로 타입이 통과한다. 런타임에서도 깊은 병합 이 일어난다. merge(defaults, { subtitle: { active: true } }) // subtitle: { active: true, color: "white" } ← color가 살아 있음 비슷한 방법으로 DeepOnly , DeepRequire 도 만들 수 있다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
2026.09.28 1일 1로그. 본 문서는 Claude를 사용해 정리했습니다. 『실무로 통하는 타입스크립트』 특정 프로퍼티만 선택형으로 만들기 목표 : 일부 프로퍼티만 선택형으로 바꾼 타입을 파생한다. 접근 : 두 객체 타입의 인터섹션 을 만드는 헬퍼 타입을 정의한다. 한 객체는 선택된 키를 선택형( ? )으로 매핑하고, 다른 객체는 나머지 프로퍼티를 그대로 매핑한다. type Person = { name: string; age: number; profession: string; }; // Partial과 달리 지정한 키만 선택형으로 매핑한다 type SelectPartial = { [P in K]?: T[P]; }; type Age = SelectPartial ; 나머지 키와 인터섹션 type…
Открыть источник