TypeScript:优化对象属性校验方案,提升错误提示友好性
优化TypeScript对象键映射函数的错误提示友好度
你的原方案已经实现了基础的键存在性检查,但错误提示仅告知参数不符合keyof CustomObj,无法直观展示合法键列表。下面提供两种优化方案,让错误提示更清晰具体:
方案1:自定义错误提示,直接列出合法键
通过条件类型和模板字面量,让无效键触发包含合法键列表的错误信息:
type SelectOption = { value: string; label: string; }; // 辅助类型:将联合类型转为元组,用于拼接合法键列表 type UnionToTuple<T> = (T extends any ? (t: T) => T : never) extends (t: infer U) => U ? [...UnionToTuple<Exclude<T, U>>, U] : []; // 生成自定义错误提示类型 type InvalidKeyError<T, K> = K extends keyof T ? never : `错误:"${K & string}" 不是 ${T} 的合法键,可选键为:${UnionToTuple<keyof T>['join'](', ')}`; const mapItem = <T extends Record<string, unknown>, K extends keyof T | string, V extends keyof T | string>( valueKey: K extends keyof T ? K : InvalidKeyError<T, K>, labelKey: V extends keyof T ? V : InvalidKeyError<T, V> ) => { return (itemToMap: T): SelectOption => ({ ...itemToMap, value: String(itemToMap[valueKey as keyof T]), label: String(itemToMap[labelKey as keyof T]), }); };
测试效果
type CustomObj = { name: string; id: number; }; const myObjects: CustomObj[] = []; const mapped = myObjects.map(mapItem('id', 'name')); // 正常编译 const mapped2 = myObjects.map(mapItem('id', 'names')); // 错误提示:Type '"names"' is not assignable to type '错误:"names" 不是 CustomObj 的合法键,可选键为:name, id'
这个方案的错误提示直接点明问题,并列出所有可选的合法键,让开发者无需额外查看类型定义就能修正参数。
方案2:同时检查键存在性与属性类型安全性
原方案中用as断言将属性转为string存在类型风险(比如id是number类型),可以在类型层面约束键对应的属性必须是可安全转为字符串的类型,并给出对应提示:
type SelectOption = { value: string; label: string; }; const mapItem = <T extends Record<string, unknown>>( valueKey: keyof T extends infer K ? K extends keyof T ? T[K] extends string | number | boolean ? K : `错误:键 "${K & string}" 对应的属性类型无法转为字符串` : never : never, labelKey: keyof T extends infer K ? K extends keyof T ? T[K] extends string | number | boolean ? K : `错误:键 "${K & string}" 对应的属性类型无法转为字符串` : never : never ) => { return (itemToMap: T): SelectOption => ({ ...itemToMap, value: String(itemToMap[valueKey]), label: String(itemToMap[labelKey]), }); };
测试效果
type CustomObj = { name: string; id: number; extra: { nested: string }; // 无法直接转为字符串的类型 }; const myObjects: CustomObj[] = []; const mapped3 = myObjects.map(mapItem('id', 'extra')); // 错误提示:Type '"extra"' is not assignable to type '错误:键 "extra" 对应的属性类型无法转为字符串'
这个方案不仅优化了键不存在的提示,还能提前拦截属性类型无法安全转为字符串的情况,避免运行时潜在问题。
内容的提问来源于stack exchange,提问作者Kremdas
相关产品推荐
相关产品推荐

