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

TypeScript:基于输入类型推导返回类型的实现问题

解决TypeScript中getHandler函数的类型关联问题

问题重现

先给出基础类型定义和错误的实现,以及触发报错的调用场景:

// 基础类型定义
type FooType = { kind: "Foo"; fooProp: string };
type BarType = { kind: "Bar"; barProp: number };
type FooBarType = FooType | BarType;

// 错误的getHandler实现
function getHandler(kind: "Foo" | "Bar") {
  switch (kind) {
    case "Foo":
      return { handle: (item: FooType) => console.log(item.fooProp) };
    case "Bar":
      return { handle: (item: BarType) => console.log(item.barProp) };
  }
}

// 调用时触发TS2345错误
const fooBar: FooBarType = Math.random() > 0.5 ? { kind: "Foo", fooProp: "test" } : { kind: "Bar", barProp: 123 };
const handler = getHandler(fooBar.kind);
handler.handle(fooBar); // 错误:类型'FooType | BarType'无法赋值给类型'never'

错误原因

TypeScript无法自动关联handler的类型与fooBar的类型:原函数返回的是联合类型{handle: (item: FooType) => void} | {handle: (item: BarType) => void},调用handle时,TS会取两个分支参数类型的交集(FooType & BarType),而这两个类型没有共同属性,交集结果为never,因此触发报错。

解决方案:用泛型绑定Kind与对应类型

通过泛型让TypeScript追踪kind参数和返回值的类型关联,具体有两种实现方式:

方式1:使用类型映射表(可读性更高)

先定义kind到对应类型的映射,再通过泛型约束实现类型关联:

type FooType = { kind: "Foo"; fooProp: string };
type BarType = { kind: "Bar"; barProp: number };
type FooBarType = FooType | BarType;

// 定义kind与对应类型的映射表
type KindMap = {
  "Foo": FooType;
  "Bar": BarType;
};

// 泛型版getHandler
function getHandler<K extends keyof KindMap>(kind: K) {
  switch (kind) {
    case "Foo":
      return { handle: (item: KindMap[K]) => console.log(item.fooProp) } as const;
    case "Bar":
      return { handle: (item: KindMap[K]) => console.log(item.barProp) } as const;
    default:
      throw new Error(`Unknown kind: ${kind}`);
  }
}

// 调用示例:无类型错误
const fooBar: FooBarType = Math.random() > 0.5 ? { kind: "Foo", fooProp: "test" } : { kind: "Bar", barProp: 123 };
const handler = getHandler(fooBar.kind);
handler.handle(fooBar);

方式2:使用条件类型(无需额外映射表)

直接通过条件类型根据kind推导对应参数类型:

type FooType = { kind: "Foo"; fooProp: string };
type BarType = { kind: "Bar"; barProp: number };
type FooBarType = FooType | BarType;

function getHandler<K extends FooBarType["kind"]>(kind: K) {
  // 根据kind推导对应的参数类型
  type ItemType = K extends "Foo" ? FooType : BarType;

  switch (kind) {
    case "Foo":
      return { handle: (item: ItemType) => console.log(item.fooProp) };
    case "Bar":
      return { handle: (item: ItemType) => console.log(item.barProp) };
    default:
      throw new Error(`Unknown kind: ${kind}`);
  }
}

// 调用示例:无类型错误
const fooBar: FooBarType = Math.random() > 0.5 ? { kind: "Foo", fooProp: "test" } : { kind: "Bar", barProp: 123 };
const handler = getHandler(fooBar.kind);
handler.handle(fooBar);

原理说明

泛型K会被推断为fooBar.kind的具体值("Foo"或"Bar"),对应的KindMap[K]或ItemType会被解析为匹配的类型(FooType或BarType)。TypeScript此时能识别出fooBar的类型与handle的参数类型完全匹配,因此不会再抛出错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:54:26