React Academy · 课时

组件变体的可辨识联合类型

使用可辨识联合类型对变体属性建模,让 TypeScript 强制检查有效的属性组合。

第 1 / 4 课13 个步骤

组件变体的可辨识联合类型 是 CoddyKit 上的免费 React Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Academy 课程共包含 4 节课。

变体属性的问题

一个组件如果使用可选属性来支持不同模式(例如,一个按钮既可以是链接,也可以是按钮),就可能出现无效的属性组合。没有判别联合类型,TypeScript 无法捕获这些问题。

什么是判别联合类型

判别联合类型是一组共享字面量类型字段(即判别字段)的联合类型。TypeScript 会根据该字段的值缩小类型范围。

type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'rectangle'; width: number; height: number };

function area(shape: Shape): number {
  if (shape.kind === 'circle') return Math.PI * shape.radius ** 2;
  return shape.width * shape.height; // TS knows width/height exist here
}

按钮与链接变体

在 as 或 variant 字段上使用判别联合类型,为多态组件建模,从而为每种情况强制执行正确的属性。

type ButtonProps =
  | { as: 'button'; onClick: () => void; disabled?: boolean; children: React.ReactNode }
  | { as: 'a'; href: string; target?: string; children: React.ReactNode };

function ActionButton(props: ButtonProps) {
  if (props.as === 'button') {
    return <button onClick={props.onClick} disabled={props.disabled}>{props.children}</button>;
  }
  return <a href={props.href} target={props.target}>{props.children}</a>;
}

Alert 组件变体

为 Alert 建模,使每种严重级别类型都具有不同的必填数据。

type AlertProps =
  | { type: 'success'; message: string }
  | { type: 'error'; message: string; onRetry: () => void }
  | { type: 'warning'; message: string; details?: string };

function Alert(props: AlertProps) {
  if (props.type === 'error') {
    return (
      <div className="alert error">
        <p>{props.message}</p>
        <button onClick={props.onRetry}>Retry</button>
      </div>
    );
  }
  return <div className={`alert ${props.type}`}>{props.message}</div>;
}

在事件处理函数中缩小类型范围

判别联合类型也适用于事件驱动的数据——非常适合状态机或基于操作的状态。

type LoadingState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error };

function DataView<T>({ state }: { state: LoadingState<T> }) {
  if (state.status === 'loading') return <Spinner />;
  if (state.status === 'error') return <p>{state.error.message}</p>;
  if (state.status === 'success') return <pre>{JSON.stringify(state.data)}</pre>;
  return null;
}

使用 never 进行穷尽检查

在默认分支中添加 never 检查。如果添加了新的联合成员却没有处理它,TypeScript 就会报告错误。

function assertNever(x: never): never {
  throw new Error('Unhandled case: ' + x);
}

function renderIcon(type: AlertProps['type']) {
  switch (type) {
    case 'success': return <CheckIcon />;
    case 'error': return <XIcon />;
    case 'warning': return <WarnIcon />;
    default: return assertNever(type); // TS error if a case is missing
  }
}

用于 API 响应的判别联合类型

将 API 响应结构建模为判别联合类型,这样调用方无需进行类型断言,就能处理成功和错误路径。

type ApiResult<T> =
  | { ok: true; data: T }
  | { ok: false; error: string; code: number };

async function fetchUser(id: string): Promise<ApiResult<User>> {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) return { ok: false, error: 'Not found', code: res.status };
  return { ok: true, data: await res.json() };
}

用于缩小类型范围的类型守卫

在判别字段不是简单相等检查的复杂场景中,可以使用自定义类型守卫来缩小联合类型的范围。

function isSuccess<T>(result: ApiResult<T>): result is { ok: true; data: T } {
  return result.ok === true;
}

const result = await fetchUser('1');
if (isSuccess(result)) {
  console.log(result.data.name); // TS knows data exists
}

避免可选属性堆积

如果不使用判别联合类型,组件就会积累一些只有在特定组合中才有效的可选属性——这既令人困惑,又缺乏类型约束。判别联合类型可以消除不可能的状态。

// Bad: optional prop soup — invalid combos allowed:
interface BadProps {
  href?: string;
  onClick?: () => void;
  disabled?: boolean;
}

// Good: only valid combos via discriminated union:
type GoodProps =
  | { as: 'a'; href: string }
  | { as: 'button'; onClick: () => void; disabled?: boolean };

组合联合类型

使用 &(交叉类型)将共享属性添加到联合类型的所有成员中。

type BaseProps = { className?: string; children: React.ReactNode };

type ButtonVariant =
  | (BaseProps & { variant: 'primary'; onClick: () => void })
  | (BaseProps & { variant: 'link'; href: string });

运行时判别

React 在运行时使用判别字段来渲染正确的界面。TypeScript 在编译时使用它来强制执行正确的属性用法。这两个层面都受到保护。

快速检查

判别联合类型中的判别字段是什么?

回顾

判别联合类型通过共享一个字面量类型的判别字段,为具有互斥属性集合的组件建模。在默认分支中使用 never 进行穷尽检查。它们消除了可选属性所允许的无效属性组合,使组件能够自我说明并确保类型安全。

免费开始

用 AI 导师学习 React — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
88
课程
324

常见问题解答

「组件变体的可辨识联合类型」课时是免费的吗?

是的 — 「组件变体的可辨识联合类型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Academy 课程共包含 4 节课。

「组件变体的可辨识联合类型」这节课中我会学到什么?

使用可辨识联合类型对变体属性建模,让 TypeScript 强制检查有效的属性组合。 你通过在浏览器中直接运行的动手代码来练习 React Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 React Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 React Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「组件变体的可辨识联合类型」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 React Academy 课中编写并运行代码吗?

能。每节 React Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 组件变体的可辨识联合类型
  2. React 中的条件类型与映射类型
  3. 带有 'as' 属性的多态组件
  4. 类型安全的表单与 API 响应契约
← 返回 React Academy