You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 08:57:39