TypeScript报错:TTextKey无法索引TOption类型,求修复方案
问题描述
代码里return option[optionTextKey]这一行触发了TypeScript错误:
Type TTextKey cannot be used to index type TOption
相关代码如下:
type Props< TTextKey, TOption extends (TTextKey extends string ? { [key in TTextKey]: string; } : string), > = ( { optionTextKey: TTextKey; options: TOption[]; } | { options: string[]; optionTextKey?: never; } ) & { name: string; placeholder: string; }; function LinkDropdown< TTextKey, TOption extends (TTextKey extends string ? { [key in TTextKey]: string; } : string), >({ options, optionTextKey, name, placeholder, }: Props<TTextKey, TOption>): ReactElement { // some code const getOptionText = (option: TOption, optionTextKey: TTextKey) => { if ('string' === typeof option) return option; // ts报错位置 return option[optionTextKey]; }; // some code }
你尝试把TOption的泛型约束改成TOption extends { [key in TTextKey]: string; }后报错消失,但不确定这是不是正确的修复方式,想知道怎么正确解决这个问题。
问题根源
原来的泛型约束TOption extends (TTextKey extends string ? { [key in TTextKey]: string; } : string)存在逻辑模糊的地方:TypeScript没办法在getOptionText函数里精准判断,当option不是字符串时,TTextKey一定是字符串类型,自然没法确定option有TTextKey对应的索引。
正确修复方案
核心思路是让TypeScript明确:传optionTextKey时,TOption肯定是包含这个键的对象;不传的时候,TOption就是字符串类型。下面给两种可行的修复方式:
方式1:拆分Props类型,明确场景
把Props拆成两种清晰的类型,分别对应字符串选项和对象选项的场景,泛型只在需要的时候生效:
// 基础公共属性 type BaseProps = { name: string; placeholder: string; }; // 字符串选项的场景:不需要optionTextKey type StringOptionProps = BaseProps & { options: string[]; optionTextKey?: never; }; // 对象选项的场景:必须传optionTextKey,且options是包含该键的对象数组 type ObjectOptionProps<TTextKey extends string> = BaseProps & { optionTextKey: TTextKey; options: { [key in TTextKey]: string }[]; }; // Props是两种场景的联合类型,默认TTextKey为never(对应字符串场景) type Props<TTextKey extends string = never> = StringOptionProps | ObjectOptionProps<TTextKey>; function LinkDropdown<TTextKey extends string = never>({ options, optionTextKey, name, placeholder, }: Props<TTextKey>): ReactElement { const getOptionText = (option: typeof options[0], optionTextKey?: TTextKey) => { if (typeof option === 'string') return option; // 这里TypeScript能明确option是包含TTextKey的对象,用!排除undefined的情况 return option[optionTextKey!]; }; // some code }
方式2:优化原泛型约束,辅助推断
如果想保留原来的泛型结构,需要让TTextKey和TOption的约束关系更紧密,同时可以用类型断言辅助TypeScript推断:
type Props< TTextKey extends string | never, TOption extends TTextKey extends string ? { [key in TTextKey]: string } : string > = ( // 用条件判断让Props的结构更明确 TTextKey extends string ? { optionTextKey: TTextKey; options: TOption[] } : { options: string[]; optionTextKey?: never } ) & { name: string; placeholder: string; }; function LinkDropdown< TTextKey extends string | never, TOption extends TTextKey extends string ? { [key in TTextKey]: string } : string >({ options, optionTextKey, name, placeholder, }: Props<TTextKey, TOption>): ReactElement { const getOptionText = (option: TOption, optionTextKey: TTextKey) => { if (typeof option === 'string') return option; // 用类型断言告诉TypeScript此时option是包含TTextKey的对象 return (option as { [key in TTextKey]: string })[optionTextKey]; }; // some code }
关于你之前的修改
你把约束改成TOption extends { [key in TTextKey]: string; }后报错消失,是因为这个约束强制TOption必须是包含TTextKey的对象,但同时也丢失了TOption为字符串类型的场景支持。如果你的业务里不需要字符串类型的选项,这个修改可以用;但如果要同时支持两种场景,上面的方案更合适。
内容的提问来源于stack exchange,提问作者nukuutos

