4.6 KiB
4.6 KiB
Query API 类型推断优化 - 支持 JSON Schema Required 字段
问题描述
之前的 query-api 实现对所有参数使用 Partial,导致即使 JSON Schema 中定义了 required 字段,TypeScript 类型推断也会将所有字段标记为可选。
示例:
const api = {
update: {
metadata: {
args: {
data: {
type: "object",
properties: {
id: { type: "string" },
domain: { type: "string" }
},
required: ["domain"] // 只有 domain 是必需的
}
}
}
}
} as const;
// 之前:所有字段都是可选的
// queryApi.update({ data: {} }) // 不会报错,但应该要求 domain
// 现在:正确识别必需字段
queryApi.update({ data: {} }) // 报错:缺少必需字段 "domain"
queryApi.update({ data: { domain: "test.com" } }) // 正确
解决方案
1. 增强 InferFromJSONSchema 类型
更新类型推断逻辑以支持 required 字段:
type InferFromJSONSchema<T> =
// ... 其他类型处理 ...
// 对象类型 - 带 required 字段(必需字段 + 可选字段)
T extends { type: "object"; properties: infer P; required: infer R extends readonly string[] }
? {
// 必需字段
[K in keyof P as K extends R[number] ? K : never]: InferFromJSONSchema<P[K]>
} & {
// 可选字段
[K in keyof P as K extends R[number] ? never : K]?: InferFromJSONSchema<P[K]>
}
: // 对象类型 - 不带 required 字段(所有字段可选)
T extends { type: "object"; properties: infer P }
? {
[K in keyof P]?: InferFromJSONSchema<P[K]>
}
: ...
2. 移除不必要的 Partial
从以下位置移除 Partial 包装:
ApiMethods类型定义QueryApi.post()方法createApi()方法内部
3. 支持 additionalProperties
添加对动态对象的支持:
// 对象类型 - additionalProperties
T extends { type: "object"; additionalProperties: infer A }
? (A extends {} ? Record<string, any> : never)
: ...
类型推断示例
示例 1:必需字段和可选字段
const api = {
update: {
metadata: {
args: {
data: {
type: "object",
properties: {
id: { type: "string" },
domain: { type: "string" },
status: {
type: "string",
enum: ["active", "inactive"]
}
},
required: ["domain"]
}
}
}
}
} as const;
const queryApi = createQueryApi({ api });
// ✅ 正确:只传必需字段
queryApi.update({ data: { domain: "test.com" } });
// ✅ 正确:传必需字段 + 可选字段
queryApi.update({
data: {
domain: "test.com",
id: "123",
status: "active"
}
});
// ❌ 错误:缺少必需字段
queryApi.update({ data: { id: "123" } });
// Error: 类型 "{ id: string; }" 中缺少属性 "domain"
// ❌ 错误:enum 值不正确
queryApi.update({
data: {
domain: "test.com",
status: "pending"
}
});
// Error: 不能将类型 "pending" 分配给类型 "active" | "inactive"
示例 2:没有 required 字段(全部可选)
const api = {
list: {
metadata: {
args: {
data: {
type: "object",
properties: {
page: { type: "number" },
pageSize: { type: "number" }
}
// 没有 required 字段
}
}
}
}
} as const;
const queryApi = createQueryApi({ api });
// ✅ 所有字段都是可选的
queryApi.list({ data: {} });
queryApi.list({ data: { page: 1 } });
queryApi.list({ data: { page: 1, pageSize: 20 } });
示例 3:动态对象 (additionalProperties)
const api = {
createMeta: {
metadata: {
args: {
metadata: {
type: "object",
additionalProperties: {} // 动态键值对
}
}
}
}
} as const;
const queryApi = createQueryApi({ api });
// ✅ 可以传入任意键值对
queryApi.createMeta({ metadata: { key1: "value1", key2: 123 } });
测试
运行测试文件验证类型推断:
npx tsc --noEmit test/verify-fix.ts
文件修改
src/query-api.ts- 更新类型推断逻辑test/verify-fix.ts- 测试用例test/type-test.ts- 类型推断测试test/debug-type.ts- 调试类型推断
注意事项
- as const - API 定义必须使用
as const以保持字面量类型 - readonly - 类型推断需要处理
readonly修饰符 - enum - 自动推断 enum 值并提供类型约束
- 构建 - 修改后需要重新构建项目:
npm run build