Created
June 30, 2024 11:52
-
-
Save lightningspirit/ba3a3997e4ce64e7ce9df358cff958f9 to your computer and use it in GitHub Desktop.
Testing PocketBase typing system in JS SDK
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PocketBase, { | |
BaseAuthStore, | |
ListResult, | |
LogService, | |
RecordFullListOptions, | |
RecordListOptions, | |
RecordModel, | |
RecordOptions, | |
RecordService, | |
} from 'pocketbase' | |
import { PageProps } from '../../../.next/types/app/(app)/(with-white-header)/page' | |
type Split< | |
S extends string, | |
D extends string, | |
> = S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S] | |
type Trim<S extends string> = S extends ` ${infer T}` | |
? Trim<T> | |
: S extends `${infer T} ` | |
? Trim<T> | |
: S | |
type NonEmptyArray<T> = T extends [infer F, ...infer R] ? T : undefined | |
type JoinUnion<T> = T extends [infer F, ...infer R] | |
? F extends string | |
? `${Trim<F>}` | JoinUnion<R> | |
: undefined | |
: never | |
type SplitStringToUnion< | |
S extends string | undefined, | |
D extends string, | |
> = S extends '' | |
? undefined | |
: S extends string | |
? NonEmptyArray<Split<S, D>> extends infer T | |
? JoinUnion<T> | |
: undefined | |
: undefined | |
type Test1 = SplitStringToUnion<'id, mimetype, updated', ','> // 'id' | 'mimetype' | 'updated' | |
type Test2 = SplitStringToUnion<'', ','> // undefined | |
type Test3 = SplitStringToUnion<undefined, ','> // undefined | |
type Test4 = SplitStringToUnion<'singleValue', ','> | |
declare function splitStringToUnion< | |
I extends string | undefined = undefined, | |
O extends I extends string | |
? SplitStringToUnion<I, ','> | |
: undefined = I extends string ? SplitStringToUnion<I, ','> : undefined, | |
>(i?: I): O | |
const withString = splitStringToUnion('id, mimetype, updated') | |
const withEmptyFields = splitStringToUnion('') | |
const withUndefined = splitStringToUnion(undefined) | |
const noOption1 = splitStringToUnion() | |
type ExtractFields<O extends RecordListOptions | undefined> = O extends { | |
fields: infer F | |
} | |
? F extends string | |
? SplitStringToUnion<F, ','> | |
: undefined | |
: undefined | |
// type ExtractFields<O extends { fields?: string }> = O extends { | |
// fields: infer F | |
// } | |
// ? F extends string | |
// ? Trim<F> extends `${infer First}, ${infer Rest}` | |
// ? Trim<First> | ExtractFields<{ fields: Trim<Rest> }> | |
// : Trim<F> | |
// : never | |
// : never | |
// type ExtractFields< | |
// O extends { [s: string]: any } | undefined = undefined, | |
// K extends keyof O | undefined = undefined, | |
// > = K extends keyof O | |
// ? O extends { [s: string]: any } | |
// ? SplitStringToUnion<O[K], ','> | |
// : undefined | |
// : undefined | |
type opt1 = ExtractFields<{ | |
fields: 'id, mimetype, updated' | |
}> | |
type opt2 = ExtractFields<{}> | |
type opt3 = ExtractFields<undefined> | |
// function extractFields< | |
// O extends Options, | |
// R = O extends { fields: infer F } | |
// ? F extends string | |
// ? SplitStringToUnion<F, ','> | |
// : undefined | |
// : undefined, | |
// >(o?: O): R | |
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ( | |
k: infer I, | |
) => void | |
? I | |
: never | |
type MergePicks<U> = | |
UnionToIntersection<U> extends infer O ? { [K in keyof O]: O[K] } : never | |
type IntersectWithFields<T, F> = F extends keyof T ? Pick<T, F> : never | |
type Test = MergePicks< | |
IntersectWithFields<Page, ExtractFields<{ fields: 'id, title, content' }>> | |
> | |
type Test12 = IntersectWithFields< | |
Page, | |
ExtractFields<{ fields: 'id, title, content' }> | |
> | |
type TestIntersect = IntersectWithFields<Page, 'title' | 'slug'> // should be { title: string; slug: string; } | |
const p = {} as Page | |
declare function intersectWithFields< | |
T extends Record<string, any>, | |
F extends { fields: string }, | |
>(obj: T, fields: F): MergePicks<IntersectWithFields<T, ExtractFields<F>>> | |
const foo = intersectWithFields< | |
Page, | |
{ fields: 'id, title, content, language' } | |
>(p, { fields: 'id, title, content, language' }) | |
type RecordMapGeneric = { | |
[s: string]: Partial<RecordModel> | |
} | |
type Mapper< | |
RMap extends RecordMapGeneric, | |
MMap extends { | |
id?: string | |
created?: Date | |
updated?: Date | |
expand?: any | |
} & { | |
[K in keyof RMap]: any | |
}, | |
> = { | |
[K in keyof RMap]: (record: RMap[K]) => MMap[K] | |
} | |
export class TypedRecordService< | |
RMap extends RecordMapGeneric, | |
MMap extends { [K in keyof RMap]: any }, | |
K extends keyof RMap, | |
> extends RecordService<MMap[K]> { | |
private mapper: Mapper<RMap, MMap> | |
private idOrName: K | |
constructor( | |
client: PocketBase, | |
collectionIdOrName: K, | |
mapper: Mapper<RMap, MMap>, | |
) { | |
super(client, collectionIdOrName.toString()) | |
this.idOrName = collectionIdOrName | |
this.mapper = mapper | |
} | |
getList< | |
O extends RecordListOptions | undefined, | |
F extends string | undefined = O extends { fields: infer T } | |
? T extends string | |
? SplitStringToUnion<T, ','> | |
: undefined | |
: undefined, | |
T extends object = F extends keyof MMap[K] ? Pick<MMap[K], F> : MMap[K], | |
// T extends O extends { fields: infer F } | |
// ? F extends string | |
// ? IntersectWithFields<MMap[K], SplitStringToUnion<F, ','>> | |
// : MMap[K] | |
// : MMap[K], | |
>(page?: number, perPage?: number, options?: O): Promise<ListResult<T>> { | |
return super.getList(page, perPage, options) | |
} | |
} | |
// TESTS | |
interface RecordPage extends RecordModel { | |
title: string | |
slug: string | |
content: string | |
language: string | |
show_in_menu: 'copyright' | 'footer' | |
priority: number | |
public: boolean | |
} | |
type RecordMap = { | |
pages: RecordPage | |
} | |
export type ModelMap = { | |
pages: Page | |
} | |
const typedRecordService = new TypedRecordService<RecordMap, ModelMap, 'pages'>( | |
new PocketBase(), | |
'pages', | |
{} as any, | |
) | |
typedRecordService | |
.getList(1, 50, { | |
fields: 'title', | |
}) | |
.then((result) => { | |
console.log(result.items.at(0)?.title) | |
}) | |
typedRecordService | |
.getList(1, 50, { | |
// no fields | |
}) | |
.then((result) => { | |
console.log(result.items.at(0)?.title) | |
}) | |
typedRecordService.getList(1, 50).then((result) => { | |
console.log(result.items.at(0)?.title) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment