最近在使用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中的InputProps和SelectProps是通过继承和映射来定义的,TypeScript 可能没有对props的结构进行严格的检查。具体来说,props是一个对象,TypeScript 在类型检查时可能会允许额外的属性存在于对象中,只要对象中包含了所需的属性。这就是为什么在Input的props中添加了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,问题显然出在props的defaultValue类型上,但错误提示却显示缺少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是一个联合类型,它可以是Input或Select类型。当 TypeScript 检查addCompParam数组中的对象时,它会尝试确保props属性符合InputProps或SelectProps的定义。
在您的示例中,您定义了一个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无法完全理解问题的核心。
如果各位掘友有遇到过类似问题并找到了解决方案,恳请在评论区分享一下,非常感谢!