You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 21:45:17