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

如何根据属性的存在/不存在筛选TypeScript联合类型

问题:TypeScript联合类型提取时Pick约束错误

我定义了一个API响应的联合类型IResponse,它的类型会根据URL查询参数变化,计划这样使用:

const data1 = await request<E<"aaa">>('/api/data/1?type=aaa');
const data2 = await request<E<"bbb">>('/api/data/1?type=bbb');

function request<T>(
  url: string
): Promise<T> {
  return fetch(url, config)
    .then((response) => response.json())
    .then((data) => data as T);
}

相关类型定义在index.ts:

//index.ts

import { paths } from "./autoGeneratedTypes";

type IResponse =
  paths["/api/data/{id}"]["get"]["responses"]["200"]["content"]["application/json"];

export type E<T extends "aaa" | "bbb"> = T extends "aaa"
  ? Omit<IResponse, "ca">
  : Pick<IResponse, "ca">;

export type F = E<"aaa">["bb"];

自动生成的类型文件autoGeneratedTypes.ts:

//autoGeneratedTypes.ts
/**
 * This file was auto-generated by openapi-typescript.
 * Do not make direct changes to the file.
 */

export interface paths {
  "/api/data/{id}": {
    /** @description get the data */
    get: {
      responses: {
        /** @description Retrieved the data successfully */
        200: {
          content: {
            "application/json": components["schemas"]["D"];
          };
        };
      };
    };
  };
}

export interface components {
  schemas: {
    D: components["schemas"]["C"] | components["schemas"]["B"];
    C: {
      ca: components["schemas"]["B"][];
    };
    B: {
      ba: string;
      bb: components["schemas"]["A"][];
    };
    A: {
      aa: string;
      ab: string | null;
    };
  };
}

当前遇到的问题:在Pick<IResponse, "ca">处报错,提示Type 'string' does not satisfy the constraint 'never'。且由于自动生成的schema名称(如C、B)可能变化,不能直接引用这些名称,需要从IResponse中正确提取对应类型的方案。


解决方案

问题根源是IResponse是C | B的联合类型,Pick<IResponse, "ca">会尝试从联合类型所有成员中提取共同存在的属性,但B类型没有ca属性,导致提取的键约束变为never,引发报错。

我们需要先从联合类型中筛选出包含目标属性的成员,再进行类型提取:

1. 定义类型工具,筛选联合类型中包含指定属性的成员

type ExtractWithProperty<T, K extends string> = T extends Record<K, any> ? T : never;

2. 修改E类型定义

export type E<T extends "aaa" | "bbb"> = T extends "aaa"
  ? Exclude<IResponse, ExtractWithProperty<IResponse, "ca">> // 筛选出不含ca的类型(即B)
  : ExtractWithProperty<IResponse, "ca">; // 筛选出含ca的类型(即C)

完整修改后的index.ts

import { paths } from "./autoGeneratedTypes";

type IResponse =
  paths["/api/data/{id}"]["get"]["responses"]["200"]["content"]["application/json"];

// 提取联合类型中包含指定属性的成员
type ExtractWithProperty<T, K extends string> = T extends Record<K, any> ? T : never;

export type E<T extends "aaa" | "bbb"> = T extends "aaa"
  ? Exclude<IResponse, ExtractWithProperty<IResponse, "ca">>
  : ExtractWithProperty<IResponse, "ca">;

export type F = E<"aaa">["bb"]; // 现在可正确获取B类型的bb属性

方案原理

  • ExtractWithProperty<IResponse, "ca">会从C | B中筛选出包含ca的C类型
  • Exclude<IResponse, ...>会排除C类型,得到B类型
  • 该方案仅依赖类型的属性结构,不受自动生成的schema名称变化影响

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:24:54