如何根据属性的存在/不存在筛选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
相关产品推荐
相关产品推荐

