import { DataOpts, Query } from "./query.ts"; import { z } from "zod"; import { createQueryByRoutes } from "./create-query/index.ts"; import { pick } from 'es-toolkit' type Pos = { path?: string; key?: string; id?: string; metadata?: { args?: Record; viewItem?: Record; url?: string; source?: string; [key: string]: any; }; } // JSON Schema 类型推断 - 使用更精确的类型匹配 type InferFromJSONSchema = // 处理带 enum 的字符串类型 T extends { type: "string"; enum: readonly (infer E)[] } ? E : T extends { type: "string"; enum: (infer E)[] } ? E : // 基础类型 T extends { type: "string" } ? string : T extends { type: "number" } ? number : T extends { type: "integer" } ? number : T extends { type: "boolean" } ? boolean : // 数组类型 T extends { type: "array"; items: infer I } ? Array> : // 对象类型 - 带 required 字段(必需字段 + 可选字段) // 注意:必须在 additionalProperties 检查之前,因为两者可能同时存在 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 } & { [K in keyof P as K extends R[number] ? never : K]?: InferFromJSONSchema } : // 对象类型 - 不带 required 字段(所有字段可选) T extends { type: "object"; properties: infer P } ? { [K in keyof P]?: InferFromJSONSchema } : // 对象类型 - 只有 additionalProperties(纯动态对象) T extends { type: "object"; additionalProperties: infer A } ? (A extends false ? Record : Record) : // 默认情况 - 如果是对象但没有 type 字段,尝试递归推断 T extends Record ? T extends { properties: infer P; required: infer R extends readonly string[] } ? { [K in keyof P as K extends R[number] ? K : never]: InferFromJSONSchema } & { [K in keyof P as K extends R[number] ? never : K]?: InferFromJSONSchema } : T extends { properties: infer P } ? { [K in keyof P]?: InferFromJSONSchema } : unknown : unknown; // 统一类型推断:支持 Zod schema 和原始 JSON Schema type InferType = T extends z.ZodType ? U : // Zod schema T extends { type: infer TType } ? InferFromJSONSchema : // 任何包含 type 字段的 JSON Schema(忽略 $schema) T extends { properties: infer P } ? InferFromJSONSchema : // 处理没有 type 但有 properties 的对象 T; // 检查是否标记为可选 type IsOptional = T extends { optional: true } ? true : false; // 提取 args 对象,将每个 Zod schema 或 JSON Schema 转换为实际类型 // 根据 optional 字段分离必需字段和可选字段 type ExtractArgsFromMetadata = T extends { metadata?: { args?: infer A } } ? A extends Record ? ( // 必需字段(没有 optional: true) { [K in keyof A as IsOptional extends true ? never : K]: InferType } & // 可选字段(有 optional: true) { [K in keyof A as IsOptional extends true ? K : never]?: InferType } ) : never : never; // 类型映射:将 API 配置转换为方法签名 type ApiMethods

= { [Path in keyof P]: { [Key in keyof P[Path]]: ( data?: ExtractArgsFromMetadata, opts?: DataOpts ) => ReturnType } } type QueryApiOpts

= { query?: Query, api?: P } export class QueryApi

{ query: Query; constructor(opts?: QueryApiOpts

) { this.query = opts?.query ?? new Query(); if (opts?.api) { this.createApi(opts.api); } } // 使用泛型来推断类型 post( pos: T, data?: ExtractArgsFromMetadata, opts?: DataOpts ) { const _pos = pick(pos, ['path', 'key', 'id']); return this.query.post({ ..._pos, payload: data }, opts) } createApi(api: P): asserts this is this & ApiMethods

{ const that = this as any; const apiEntries = Object.entries(api); const keepPaths = ['createApi', 'query', 'post']; for (const [path, methods] of apiEntries) { if (keepPaths.includes(path)) continue; // 为每个 path 创建命名空间对象 if (!that[path]) { that[path] = {}; } for (const [key, pos] of Object.entries(methods)) { that[path][key] = (data?: ExtractArgsFromMetadata, opts: DataOpts = {}) => { const _pos = pick(pos, ['path', 'key', 'id']); if (pos.metadata?.viewItem?.api?.url && !opts.url) { opts.url = pos.metadata.viewItem.api.url; } return that.query.post({ ..._pos, payload: data }, opts); }; } } } } // 创建工厂函数,提供更好的类型推断 export function createQueryApi

( opts?: QueryApiOpts

): QueryApi

& ApiMethods

{ return new QueryApi(opts) as QueryApi

& ApiMethods

; } export { createQueryByRoutes }; // const demo = { // "test_path": { // "test_key": { // "path": "demo", // "key": "test", // metadata: { // args: { // name: z.string(), // age: z.number(), // } // } // } // } // } as const; // // 方式1: 使用工厂函数创建(推荐) // const queryApi = createQueryApi({ query: new Query(), api: demo }); // // 现在调用时会有完整的类型推断 // // data 参数会被推断为 { name?: string, age?: number } // queryApi.test_path.test_key({ name: "test", age: 18 }); // // 也可以不传参数 // queryApi.test_path.test_key(); // // 或者只传递 opts // queryApi.test_path.test_key(undefined, { timeout: 5000 });