Next.js 14中shadcn/ui ToggleGroup组件类型不兼容问题求助
解决shadcn/ui ToggleGroup组件类型不兼容问题
问题描述
在Next.js 14项目中使用shadcn/ui的ToggleGroup组件时,尝试将type属性设置为"single"或"multiple"会触发类型不兼容错误:
Types of property 'type' are incompatible.
Type '"single" | "multiple"' is not assignable to type '"multiple"'.
Type '"single"' is not assignable to type '"multiple"'.(2322)
核心原因是:shadcn/ui的ToggleGroup在两种模式下,onValueChange的参数类型不同——single模式返回单个字符串(或undefined),multiple模式返回字符串数组。自定义组件统一使用(value: string) => void作为onChange类型,导致TypeScript无法匹配对应模式的类型要求。
解决方案
1. 修改自定义ToggleGroup组件:用泛型区分类型
通过泛型根据type自动推导onChange的参数类型,同时对齐shadcn/ui的原生类型:
import React from 'react'; import { ToggleGroup, ToggleGroupItem, ToggleGroupProps } from '@/components/ui/toggle-group'; type ToggleGroupType = 'single' | 'multiple'; interface ToggleGroupComponentProps<T extends ToggleGroupType> { label: string; options: Record<string, string>; type: T; onChange: T extends 'single' ? (value: string | undefined) => void : (value: string[]) => void; defaultValue?: T extends 'single' ? string : string[]; } const ToggleGroupComponent = <T extends ToggleGroupType>({ label, options, type, onChange, defaultValue, }: ToggleGroupComponentProps<T>) => { return ( <div className="mb-6"> <label className="block text-gray-700 text-md font-bold mb-2"> {label} </label> <ToggleGroup className="p-0 flex flex-wrap items-start justify-start" type={type} size="lg" onValueChange={onChange as ToggleGroupProps['onValueChange']} defaultValue={defaultValue as ToggleGroupProps['defaultValue']} > {Object.entries(options).map(([key, value]) => ( <ToggleGroupItem variant="outline" key={key} value={key} className="mr-2 mb-2 bg-orange-100 text-sky-950" > {value} </ToggleGroupItem> ))} </ToggleGroup> </div> ); }; export default ToggleGroupComponent;
2. 更新Home组件的使用逻辑
根据不同模式调整state结构和回调处理,匹配参数类型:
'use client'; import React, { useState } from 'react'; import { Button } from '@/components/ui/button'; import ToggleGroupComponent from '@/components/ToggleGroupComponent'; const FruitPreferences = { APPLE: 'Apple', BANANA: 'Banana', ORANGE: 'Orange', GRAPE: 'Grape', }; const ExerciseTypes = { YOGA: 'Yoga', RUNNING: 'Running', SWIMMING: 'Swimming', CYCLING: 'Cycling', }; const HobbyChoices = { READING: 'Reading', PAINTING: 'Painting', GARDENING: 'Gardening', COOKING: 'Cooking', MEDITATION: 'Some Meditation Technique', }; export default function Home() { const [formData, setFormData] = useState({ fruitPreference: [], // multiple模式用数组存储 exerciseType: '', // single模式用字符串存储 hobbyChoice: [], }); const handleToggleChange = <T extends string | string[]>(label: string, value: T) => { setFormData((prevState) => ({ ...prevState, [label]: value, })); }; const handleSubmit = async () => { console.log(formData); }; return ( <main className="flex min-h-screen flex-col items-center justify-between p-24"> <div className="bg-gray-200"> <section className="max-w-7xl mx-auto py-2"> <div className="container mx-auto max-w-6xl flex flex-col justify-start mt-12"> <h3 className="text-2xl text-gray-800 mb-8">Preferences</h3> <ToggleGroupComponent label="Fruit Preference" options={FruitPreferences} type="multiple" onChange={(value) => handleToggleChange('fruitPreference', value)} /> <ToggleGroupComponent label="Exercise Type" options={ExerciseTypes} type="single" onChange={(value) => handleToggleChange('exerciseType', value || '')} /> <ToggleGroupComponent label="Hobby Choice" options={HobbyChoices} type="multiple" onChange={(value) => handleToggleChange('hobbyChoice', value)} /> <Button onClick={handleSubmit} className="mt-4"> Submit </Button> </div> </section> </div> </main> ); };
关键说明
- 泛型
T会根据传入的type自动推断onChange的参数类型,彻底解决类型不匹配问题 - 使用类型断言
as ToggleGroupProps['onValueChange']让TypeScript接受泛型推导的回调函数 - 针对
single模式处理undefined情况(用户取消选择时触发),保证state类型一致性
内容的提问来源于stack exchange,提问作者Rasik
相关产品推荐
相关产品推荐

