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

如何让TypeScript在GET端点添加DATA属性时触发编译错误?

TypeScript 强类型API端点:限制GET请求不能包含data属性

我正在前端客户端对API端点做强类型定义,现有代码如下:

type URI<T extends string> = { url: T }
type QUERY<T> = { query: { [K in keyof T]: T[K] | T[K][] } }
type DATA<T> = { data: Required<{ [K in keyof T]: T[K] }> }
type PATCHDATA<T> = { data: { [K in keyof T]: T[K] } }
type RESPONSE<T> = { response: { [K in keyof T]: T[K] } }
type ENDPOINT = URI<any> & Partial<QUERY<any> & DATA<any> & PATCHDATA<any> & RESPONSE<any>>;

type GET<T extends ENDPOINT> = T
type POST<T extends ENDPOINT> = T;

type GET_ACCOUNT = GET<DATA<{}> & URI<'account/:id'> & QUERY<{ userId: string }> & RESPONSE<{}>>
type GET_ACCOUNTS = GET<URI<'accounts'> & QUERY<{ firstName: string, lastName: string }> & RESPONSE<{ id: string, firstname: string, lastName: string }>>

type CREATE_ACCOUNT = POST<QUERY<{}> & URI<'accounts'> & DATA<{}> & RESPONSE<{}>>

type GETS = GET_ACCOUNT | GET_ACCOUNTS
type POSTS = CREATE_ACCOUNT 

type ACCOUNT_ENDPOINTS = GETS | POSTS

需求是:当尝试给GET端点添加DATA(比如GET_ACCOUNT里的DATA<{}>)时,编译器要抛出类似“GET请求不能拥有data属性”的错误。我试过下面两种写法,但都没效果:

type GET<T extends Omit<ENDPOINT, 'data'> = T

type GET<T extends Pick<ENDPOINT, Exclude<keyof ENDPOINT, 'data'>>> = T

请问该怎么实现?


问题原因

之前的写法无效是因为ENDPOINT里的data是Partial类型(可选属性),Omit或Pick只是移除了data的定义,但当你把DATA<{}>(带有明确data属性的类型)和其他类型交叉时,最终的T仍然会包含data,原约束无法识别这种情况。

正确实现方式

方式一:带错误提示的条件类型检查

通过条件类型判断T是否包含data属性,一旦包含就返回带错误信息的类型,让编译器触发明确报错:

// 定义错误提示类型
type ApiError<T extends string> = { __errorMessage: T };

type URI<T extends string> = { url: T }
type QUERY<T> = { query: { [K in keyof T]: T[K] | T[K][] } }
type DATA<T> = { data: Required<{ [K in keyof T]: T[K] }> }
type PATCHDATA<T> = { data: { [K in keyof T]: T[K] } }
type RESPONSE<T> = { response: { [K in keyof T]: T[K] } }
type ENDPOINT = URI<any> & Partial<QUERY<any> & DATA<any> & PATCHDATA<any> & RESPONSE<any>>;

// 检查T是否包含data属性,包含则返回错误类型
type GET<T extends ENDPOINT> = T extends { data: any } 
  ? ApiError<"GET请求不能拥有data属性"> 
  : T;

type POST<T extends ENDPOINT> = T;

// 此处会触发错误:类型为带有__errorMessage的错误类型
type GET_ACCOUNT = GET<DATA<{}> & URI<'account/:id'> & QUERY<{ userId: string }> & RESPONSE<{}>>
// 正常通过检查
type GET_ACCOUNTS = GET<URI<'accounts'> & QUERY<{ firstName: string, lastName: string }> & RESPONSE<{ id: string, firstname: string, lastName: string }>>

type CREATE_ACCOUNT = POST<QUERY<{}> & URI<'accounts'> & DATA<{}> & RESPONSE<{}>>

type GETS = GET_ACCOUNT | GET_ACCOUNTS
type POSTS = CREATE_ACCOUNT 

type ACCOUNT_ENDPOINTS = GETS | POSTS

方式二:严格类型约束

直接通过类型约束禁止T包含data属性,触发类型不匹配错误:

type URI<T extends string> = { url: T }
type QUERY<T> = { query: { [K in keyof T]: T[K] | T[K][] } }
type DATA<T> = { data: Required<{ [K in keyof T]: T[K] }> }
type PATCHDATA<T> = { data: { [K in keyof T]: T[K] } }
type RESPONSE<T> = { response: { [K in keyof T]: T[K] } }
type ENDPOINT = URI<any> & Partial<QUERY<any> & DATA<any> & PATCHDATA<any> & RESPONSE<any>>;

// 约束T不能包含data属性(data只能是undefined或不存在)
type GET<T extends ENDPOINT & { data?: never }> = T;

type POST<T extends ENDPOINT> = T;

// 此处直接触发类型不匹配错误
type GET_ACCOUNT = GET<DATA<{}> & URI<'account/:id'> & QUERY<{ userId: string }> & RESPONSE<{}>>
type GET_ACCOUNTS = GET<URI<'accounts'> & QUERY<{ firstName: string, lastName: string }> & RESPONSE<{ id: string, firstname: string, lastName: string }>>

type CREATE_ACCOUNT = POST<QUERY<{}> & URI<'accounts'> & DATA<{}> & RESPONSE<{}>>

type GETS = GET_ACCOUNT | GET_ACCOUNTS
type POSTS = CREATE_ACCOUNT 

type ACCOUNT_ENDPOINTS = GETS | POSTS

效果说明

  • 给GET传入带有DATA的类型时,编译器会立即报错,明确提示问题。
  • 无data属性的正常GET请求可以顺利通过类型检查。
  • POST请求不受任何限制,依然可以正常使用DATA属性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:37:08