如何在TypeScript中正确映射深层嵌套键与对应值
问题原因
当你将类型移入泛型后,TypeScript的结构类型兼容性允许FIELDS包含额外属性。FIELDS extends Partial<MatchFormField<STATE>>仅要求FIELDS的属性值符合FormField结构,但未限制键必须是Path<STATE>的成员。TypeScript会推断FIELDS为包含未知字段的类型,而非自动收窄到合法字段集合。
解决方案
需要明确约束FIELDS的键只能是Path<STATE>的子集,以下两种方案均可实现:
方案1:移除独立的FIELDS泛型参数
直接将fields的类型设为Partial<MatchFormField<STATE>>,让TypeScript严格检查键的合法性:
import type { Path, PathValue } from '@clickbar/dot-diver' type FormField<VALUE> = { initialValue: VALUE } type MatchFormField<STATE> = { [FIELD in Path<STATE>]-?: FormField<PathValue<STATE, FIELD>> } export type FormConfig<STATE> = { id: string initialValues: STATE fields?: Partial<MatchFormField<STATE>> } declare function createForm<STATE>(config: FormConfig<STATE>): void createForm({ id: 'test-form', initialValues: { uid: 'abcd', profile: { name: 'Mark', age: 44, } }, fields: { uid: { initialValue: '00000' }, 'profile.age': { initialValue: 99 }, // 现在会被正确标记为无效 unknownField: { initialValue: 'matteo' }, }, })
方案2:保留FIELDS泛型并约束键类型
如果需要保留FIELDS的具体类型(比如在返回值中使用),可以通过泛型约束明确FIELDS的键必须是Path<STATE>的成员:
import type { Path, PathValue } from '@clickbar/dot-diver' type FormField<VALUE> = { initialValue: VALUE } type MatchFormField<STATE> = { [FIELD in Path<STATE>]-?: FormField<PathValue<STATE, FIELD>> } export type FormConfig<STATE, FIELDS extends Record<keyof FIELDS, Path<STATE>>> = { id: string initialValues: STATE fields?: FIELDS & Partial<MatchFormField<STATE>> } declare function createForm< STATE, FIELDS extends Record<keyof FIELDS, Path<STATE>> >(config: FormConfig<STATE, FIELDS>): void createForm({ id: 'test-form', initialValues: { uid: 'abcd', profile: { name: 'Mark', age: 44, } }, fields: { uid: { initialValue: '00000' }, 'profile.age': { initialValue: 99 }, // 现在会被正确标记为无效 unknownField: { initialValue: 'matteo' }, }, })
效果验证
两种方案都会让unknownField被TypeScript正确标记为无效,因为fields的键被严格限制为initialValues中存在的嵌套路径(通过Path<STATE>约束)。
内容的提问来源于stack exchange,提问作者cef62
相关产品推荐
相关产品推荐

