React组件转TypeScript遇TS7053错误:string|number无法索引对象
React转TypeScript:解决variantStyles索引类型错误
问题重现
将React组件转为TypeScript时,使用variantStyles[variant][color]触发以下类型错误:
Element implicitly has an 'any' type because expression of type 'string | number' can't be used to index type '{ solid: { slate: string; blue: string; white: string; }; outline: { slate: string; white: string; }; }'. No index signature with a parameter of type 'string' was found on type '{ solid: { slate: string; blue: string; white: string; }; outline: { slate: string; white: string; }; }'.ts(7053)
问题原因
- 类型关联缺失:当前
ButtonProps中color的类型是所有variant下颜色键的联合类型('slate' | 'blue' | 'white'),但TypeScript无法识别variant和color的对应关系——比如variant='outline'时color='blue'是非法的,但类型系统允许这种情况,导致索引时类型不匹配。 - 类型定义宽泛:
baseStyles使用了{[baseStyle: string]: string}的索引签名,让TypeScript无法推断出精确的键类型('solid' | 'outline'),而是把键视为宽泛的string,进一步加剧了索引类型问题。
解决方案
通过泛型建立variant与color的类型关联,同时优化类型定义让TypeScript能精确推断索引类型:
修正后的完整代码
import Link from 'next/link' import clsx from 'clsx' import React, { AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react'; // 从variantStyles提取精确类型,自动同步后续修改 type VariantStyles = typeof variantStyles; type Variant = keyof VariantStyles; // 泛型约束:根据传入的Variant,返回对应的合法Color类型 type Color<V extends Variant> = keyof VariantStyles[V]; // 泛型Props:关联variant和color的类型,同时兼容Link/button的原生属性 type ButtonProps<V extends Variant = 'solid'> = { variant?: V; color?: Color<V>; className?: string; href?: string; } & (V extends unknown ? (NonNullable<ButtonProps<V>['href']> extends string ? AnchorHTMLAttributes<HTMLAnchorElement> : ButtonHTMLAttributes<HTMLButtonElement>) : never); // 去掉宽泛的索引签名,用as const让TypeScript推断精确的字面量类型 const baseStyles = { solid: 'group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2', outline: 'group inline-flex ring-1 items-center justify-center rounded-full py-2 px-4 text-sm focus:outline-none', } as const; const variantStyles = { solid: { slate: 'bg-slate-900 text-white hover:bg-slate-700 hover:text-slate-100 active:bg-slate-800 active:text-slate-300 focus-visible:outline-slate-900', blue: 'bg-blue-600 text-white hover:text-slate-100 hover:bg-blue-500 active:bg-blue-800 active:text-blue-100 focus-visible:outline-blue-600', white: 'bg-white text-slate-900 hover:bg-blue-50 active:bg-blue-200 active:text-slate-600 focus-visible:outline-white', }, outline: { slate: 'ring-slate-200 text-slate-700 hover:text-slate-900 hover:ring-slate-300 active:bg-slate-100 active:text-slate-600 focus-visible:outline-blue-600 focus-visible:ring-slate-300', white: 'ring-slate-700 text-white hover:ring-slate-500 active:ring-slate-700 active:text-slate-400 focus-visible:outline-white', }, } as const; export function Button<V extends Variant = 'solid'>({ variant = 'solid' as V, color = 'slate' as Color<V>, className, href, ...props }: ButtonProps<V>) { const computedClass = clsx( baseStyles[variant], variantStyles[variant][color], className ); return href ? ( <Link href={href} className={computedClass} {...props} /> ) : ( <button className={computedClass} {...props} /> ); }
关键修改点说明
- 泛型类型关联:
ButtonProps使用泛型V extends Variant,让color的类型自动适配当前variant的合法选项——比如选择variant='outline'时,color只能是'slate' | 'white',杜绝非法组合。 - 精确类型推断:给
baseStyles和variantStyles添加as const,让TypeScript把它们的键推断为字面量类型(如'solid'而非string),确保索引操作的类型安全性。 - 默认值类型兼容:通过
as V和as Color<V>处理默认值的类型断言,保证默认值与泛型约束兼容。
内容的提问来源于stack exchange,提问作者Klim Yadrintsev
相关产品推荐
相关产品推荐

