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

如何通过对象映射React组件收窄TypeScript联合类型,替代switch?

解决TypeScript组件映射的类型收窄问题

不用switch语句的话,可以通过给组件映射表定义精确的关联类型,让TypeScript自动识别object的具体类型,或者用泛型组件实现更优雅的类型推导。

方案一:给组件映射表绑定精确类型

通过定义一个类型映射,把type字段值和对应的对象类型关联起来,再让组件映射表遵循这个类型约束,TypeScript就能自动完成类型收窄:

import React from 'react'

interface Foo {
    type: 'foo'
}

interface Bar {
    type: 'bar'
}

// 定义type值到对象类型的映射
type TypeToObjectMap = {
  foo: Foo;
  bar: Bar;
};

type ObjectTypes = TypeToObjectMap[keyof TypeToObjectMap];

const FooForm = ({ object }: { object: Foo }) => {
    return <form>I'm a form for foos</form>
}

const BarForm = ({ object }: { object: Bar }) => {
    return <form>I'm a form for bars</form>
}

// 定义组件映射表,严格遵循TypeToObjectMap的类型约束
const componentMap: {
  [K in keyof TypeToObjectMap]: React.FC<{ object: TypeToObjectMap[K] }>;
} = {
  foo: FooForm,
  bar: BarForm,
};

const ObjectForm = ({ object }: { object: ObjectTypes }) => {
    const Form = componentMap[object.type as keyof TypeToObjectMap];
    // 此时TypeScript能自动识别object的类型与Form的props匹配
    return <Form object={object} />
}

方案二:泛型组件实现类型推导

把ObjectForm定义为泛型组件,让TypeScript根据传入的object类型自动推断对应的表单组件:

import React from 'react'

interface Foo {
    type: 'foo'
}

interface Bar {
    type: 'bar'
}

type ObjectTypes = Foo | Bar;

const FooForm = ({ object }: { object: Foo }) => {
    return <form>I'm a form for foos</form>
}

const BarForm = ({ object }: { object: Bar }) => {
    return <form>I'm a form for bars</form>
}

// 泛型版本的组件映射表,用as const锁定类型
const componentMap = {
  foo: FooForm,
  bar: BarForm,
} as const;

// 泛型组件,T约束为ObjectTypes的子集
const ObjectForm = <T extends ObjectTypes>({ object }: { object: T }) => {
    const Form = componentMap[object.type];
    return <Form object={object} />;
};

这两种方案都避免了switch语句的冗余代码,同时让TypeScript能正确收窄类型,不会出现类型错误。

内容的提问来源于stack exchange,提问作者user1032752

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 03:23:21