如何声明TypeScript类型以确保其为另一类型的可赋值子集
解决TypeScript merge函数的严格类型约束问题
先明确你遇到的核心问题:
- 用
Partial<T>的写法会错误允许给必填字段传undefined,比如merge(human, {Name: undefined})会被放行 - 第二种写法存在语法错误,
T extends U不能直接作为参数类型使用
要实现你预期的约束规则——必填字段只能传有效值、可选字段可传有效值或undefined、禁止传入原类型以外的字段——我们需要自定义更严格的Partial类型:
// 辅助类型:提取T中的所有必填字段名 type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T]; // 辅助类型:提取T中的所有可选字段名 type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>; // 定义严格的Partial类型: // - 必填字段只能传原类型(排除undefined) // - 可选字段可传原类型或undefined // - 仅允许传入T中存在的字段 type StrictMergePartial<T> = Partial<{ [K in keyof T]: K extends RequiredKeys<T> ? Exclude<T[K], undefined> : T[K] | undefined }>; // 最终的merge函数 const merge = <T>(full: T, partial: StrictMergePartial<T>): T => ({ ...full, ...partial }); // 测试用例验证 interface Person { Name: string; Age: number; Occupation?: string; } const human: Person = { Name: "John", Age: 35, }; merge(human, { Age: 5 }); // ✓ 正常通过 merge(human, { Name: "Marc" }); // ✓ 正常通过 merge(human, { Name: undefined }); // ❌ 报错:undefined无法赋值给string类型 merge(human, { Occupation: "Fishermen" }); // ✓ 正常通过 merge(human, { Occupation: undefined }); // ✓ 正常通过 merge(human, { Beer: 5 }); // ❌ 报错:存在未知字段Beer
类型逻辑说明:
RequiredKeys<T>:通过判断字段是否可从类型中移除,精准提取所有必填字段的名称StrictMergePartial<T>:针对必填字段排除undefined类型,确保传入值有效;对可选字段保留undefined支持;最后用Partial让所有字段变为可选(符合部分更新的场景)- 函数返回值仍为
T,保证合并后的结果完全符合原类型结构
这样就能完全匹配你预期的所有测试用例要求了。
内容的提问来源于stack exchange,提问作者user2475269
相关产品推荐
相关产品推荐

