一个困扰我许久的TypeScript定义问题

最近在使用TypeScript定义函数参数时,我遇到了一个难题,这让我感到困扰。我猜想这可能是因为我对TypeScript的掌握不够深入。虽然我问了无数次ChatGPT,但未能获得满意的解答。至今还没解决方案。今天我将这个问题分享出来,希望能听到大家的意见,看看是否有解决方案。

我在定义addComp函数时,设置了一个参数,该参数是一个数组。数组的每一项包含两个属性:type表示组件的类型,props包括组件的可配置属性。不同组件的props有共同的也有独特的属性,例如:

js 复制代码
  [
    {
      type: 'Input',
      props: {
        required: true,
        defaultValue: '默认值',
        placeholder: '请输入',
      },
    },
    {
      type: 'Select',
      props: {
        required: true,
        defaultValue: ['1'],
        placeholder: '请输入',
        options: [
          {
            label: '选项一',
            value: '1'
          }
        ]
      },
    },
  ]

我对以上数据做了如下的TypeScript。

ts 复制代码
// 定义 props 属性的基础接口
interface CompProps {
  required?: boolean;
  placeholder?: string;
}

// 定义特定控件类型的 props 接口
interface InputProps extends CompProps {
  defaultValue?: string;
}

interface SelectProps extends CompProps {
  defaultValue?: string[] | number[];
  options: Array<{ label: string, value: string }>;
}

type CompPropsMap = {
  Input: InputProps;
  Select: SelectProps;
};

// 定义组件类型的联合类型
type CompType = 'Input' | 'Select';

// 定义 AddCompParam 中每一项的接口
interface AddCompParam<T extends CompType> {
  type: T;
  props: CompPropsMap[T];
}

然而,在使用过程中发现了问题。例如,以下示例应该报错,但实际上并没有:

ts 复制代码
// 示例对象
const addCompParam: AddCompParam<CompType>[] =
  [
    {
      type: 'Input',
      props: {
        required: true,
        defaultValue: ['默认值'],// 这里应为string,错误地使用了string[]
        placeholder: '请输入',
        options: [],// 这是一个无关属性,不应存在于InputProps中
      },
    },
];

按理论来推动,此时应该会报错,但是实际不会报错。我尝试开启严格模式,重启TypeScript服务,还是不会报错。问 ChatGPT,得到的答复是

在您的例子中,AddCompParam 的定义是基于泛型的,因此 TypeScript 会根据传入的类型进行推断。由于 CompPropsMap 中的 InputPropsSelectProps 是通过继承和映射来定义的,TypeScript 可能没有对 props 的结构进行严格的检查。具体来说,props 是一个对象,TypeScript 在类型检查时可能会允许额外的属性存在于对象中,只要对象中包含了所需的属性。这就是为什么在 Inputprops 中添加了 options 属性,TypeScript 仍然不会报错。

我对上面的答复不是很满意。为了让TypeScript 能够报错,我换了一种定义方式

ts 复制代码
// 定义 props 属性的基础接口
interface CompProps {
    required?: boolean;
    placeholder?: string;
}

// 定义特定控件类型的 props 接口
interface InputProps extends CompProps {
    defaultValue?: string;
}

interface SelectProps extends CompProps {
    defaultValue?: string[] | number[];
    options: Array<{ label: string, value: string }>;
}

// 定义 AddCompParam 中每一项的接口
type AddCompParam = 
{ type: 'Input'; props: InputProps } | 
{ type: 'Select'; props: SelectProps };

然而,在实际使用过程中,我发现了另一个严重的问题:错误信息的提示并不准确。

ts 复制代码
// 示例对象
const addCompParam: AddCompParam[] =
  [
    {
      type: 'Input',
      props: {
        required: true,
        defaultValue: ['1'],
        placeholder: '请输入',
      },
    },
];

报错信息如下所示:

不能将类型{ required: true; defaultValue: string[]; placeholder: string; }分配给类型InputProps | SelectProps

类型 { required: true; defaultValue: string[]; placeholder: string; } 中缺少属性 options,但类型 SelectProps 中需要该属性。

这个错误提示令我困惑。虽然我明确将type设置为Input,问题显然出在propsdefaultValue类型上,但错误提示却显示缺少options属性。尽管我尝试补全了这个属性,错误提示改变了,但仍未指向defaultValue的类型问题。

ts 复制代码
// 示例对象

const addCompParam: AddCompParam[] =
  [
    {
      type: 'Input',
      props: {
        required: true,
        defaultValue: ['1'],
        placeholder: '请输入',
        options:[]
      },
    },
];

报错信息如下所示:

不能将类型{ type: "Input"; props: { required: true; placeholder: string; defaultValue: string[]; options: { label: string; value: string; }[]; }; }分配给类型AddCompParam

属性props的类型不兼容。

对象字面量只能指定已知属性,并且options不在类型InputProps中。

我再次询问 ChatGPT 得到的答复是

您遇到的错误提示是由于 TypeScript 处理联合类型时的类型推断机制。在您的代码中,AddCompParam 是一个联合类型,它可以是 InputSelect 类型。当 TypeScript 检查 addCompParam 数组中的对象时,它会尝试确保 props 属性符合 InputPropsSelectProps 的定义。
在您的示例中,您定义了一个 Input 类型的对象,但是在 props 中,您传递了一个 defaultValue 的值为 ['1'],这是一个字符串数组(string[]),而 InputProps 中的 defaultValue 应该是一个字符串(string)。因此,TypeScript 在尝试推断 props 的类型时,发现 defaultValue 的类型不匹配。
由于 props 的类型是 InputProps | SelectProps,TypeScript 会将 props 视为可能是 SelectProps 类型,因此它会检查 SelectProps 的所有必需属性,包括 options。这就是为什么您会看到提示缺少 options 属性。

目前我还不确定如何解决这个问题。如果按照下面的方式定义:

ts 复制代码
type AddCompParam = [ 
  { type: 'Input'; props: InputProps }, 
  { type: 'Select'; props: SelectProps }
];

这种定义方式似乎限制了addCompParam参数中每个项的固定位置,例如type: 'Select'必须位于addCompParam数组的第二个位置。这并不是我想要的效果。

我尝试了多种方式向ChatGPT描述这个问题,但是得到的回答都未能提供正确的解决方案。可能是我的问题描述不够清晰,导致ChatGPT无法完全理解问题的核心。

如果各位掘友有遇到过类似问题并找到了解决方案,恳请在评论区分享一下,非常感谢!

相关推荐
还是大剑师兰特38 分钟前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解38 分钟前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~44 分钟前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django
张张打怪兽1 小时前
css-50 Projects in 50 Days(3)
前端·css