agnostic-query
Version:
Type-safe fluent builder for portable query schemas. Runtime-agnostic, database-agnostic — the same QuerySchema drives Drizzle, Kysely, db0, or raw SQL.
75 lines (70 loc) • 2.02 kB
text/typescript
/**
* 从列名到目标字段的完整顺序路径。
*
* 首段为列名,后续每段对应一层键或索引,不可重排或省略。
* 适配器根据路径中段的类型决定 SQL 文法:
* - 列名后全是数字 → PG 数组下标(1-indexed)
* - 列名后有字符串 → JSONB 操作符(`->` / `->>`)
*
* @example
* `["name"]` → 列 `name`
* `["address", "city"]` → 列 `address` → 键 `city`
* `["tags", 0, "name"]` → 列 `tags` → 数组索引 0 → 键 `name`
*/
export type FieldPath = [string, ...(string | number)[]];
export type SchemaShape = Record<string, any>;
/** (包含中间节点和叶子节点)
* 但 目前在 在适配器层面 没有支持任意节点, 只支持 根节点和叶子节点
*/
export type FieldPathByShape<TShape extends SchemaShape = SchemaShape> =
TShape extends SchemaShape
? {
[K in keyof TShape]: TShape[K] extends any[]
?
| [K]
| [
K,
number,
...FieldPathByShape<
TShape[K][number] extends SchemaShape
? TShape[K][number]
: never
>,
]
| [K, number]
: TShape[K] extends SchemaShape
? [K] | [K, ...FieldPathByShape<TShape[K]>]
: [K];
}[keyof TShape]
: never;
// demo
// type DemoShape = {
// id: number;
// name: string;
// tags: { id: number; name: string }[];
// category: string[];
// address: {
// city: {
// name: string;
// };
// };
// };
// type DemoField = FieldPathByShape<DemoShape>;
// type DemoField2 = FieldPathByShape<Record<string, any>>;
/** 根据完整路径提取对应位置的类型 */
export type GetPathType<T, P extends readonly any[]> = P extends readonly [
infer First,
...infer Rest,
]
? First extends keyof T
? Rest extends []
? T[First]
: GetPathType<T[First], Rest>
: First extends number // 处理数组索引
? T extends (infer R)[]
? Rest extends []
? R
: GetPathType<R, Rest>
: never
: never
: T;