diff --git a/index.d.ts b/index.d.ts index 8cb1340..fbd11b3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -77,26 +77,26 @@ declare module "flexsearch" { * **Document:** * * Search options: https://github.com/nextapps-de/flexsearch#search-options */ - type SearchOptions = { + export type SearchOptions = { query?: string; limit?: number; offset?: number; suggest?: boolean; resolution?: number; context?: boolean; - resolve?: boolean; + resolve?: R; }; /** * **Document:** * * The document descriptor: https://github.com/nextapps-de/flexsearch#the-document-descriptor */ - type DocumentDescriptor = { + type DocumentDescriptor = { id?: string | "id"; - field?: FieldName | FieldName[] | FieldOptions | Array; - index?: FieldName | FieldName[] | FieldOptions | Array; - tag?: FieldName | FieldName[] | TagOptions | Array; - store?: FieldName | FieldName[] | StoreOptions | Array | boolean; + field?: FieldName | FieldName[] | FieldOptions | Array>; + index?: FieldName | FieldName[] | FieldOptions | Array>; + tag?: FieldName | FieldName[] | TagOptions | Array>; + store?: FieldName | FieldName[] | StoreOptions | Array | boolean; }; type WorkerURL = string; @@ -104,7 +104,15 @@ declare module "flexsearch" { type WorkerConfigURL = string; type WorkerConfigPath = string; type SerializedFunctionString = string; - type FieldName = string; + type FieldName = D extends object + ? { + [K in keyof D]: K extends string + ? D[K] extends Array + ? `${ K }` | `${ K }[]:${ FieldName & string }` + : K | `${ K }:${ FieldName & string }` + : never + }[keyof D] + : never /** * The template to be applied on matches (e.g. "\$1\"), where \$1 is a placeholder for the matched partial */ @@ -116,26 +124,25 @@ declare module "flexsearch" { * * Language: https://github.com/nextapps-de/flexsearch#languages */ - global { type EncoderOptions = { rtl?: boolean; dedupe?: boolean; include?: EncoderSplitOptions; exclude?: EncoderSplitOptions; - split?: string|RegExp|""|false; + split?: string | RegExp | "" | false; numeric?: boolean; - normalize?: boolean|((str: string) => string); + normalize?: boolean | ((str: string) => string); prepare?: (str: string) => string; finalize?: (terms: string[]) => string[]; - filter?: Set|((term: string) => boolean); + filter?: Set | ((term: string) => boolean); matcher?: Map; mapper?: Map; stemmer?: Map; - replacer?: [string|RegExp, string|""]; + replacer?: [ string | RegExp, string | "" ]; minlength?: number; maxlength?: number; - cache?: boolean|number; - }} + cache?: boolean | number; + }; type EncoderSplitOptions = { letter?: boolean; @@ -143,7 +150,7 @@ declare module "flexsearch" { symbol?: boolean; punctuation?: boolean; control?: boolean; - char?: string|string[]; + char?: string | string[]; }; export const Charset: { @@ -171,7 +178,7 @@ declare module "flexsearch" { * * Right-To-Left: https://github.com/nextapps-de/flexsearch/doc/encoder.md#right-to-left-support * * Language: https://github.com/nextapps-de/flexsearch/doc/encoder.md#built-in-language-packs */ - type IndexOptions = { + type IndexOptions = { preset?: Preset; tokenize?: Tokenizer; cache?: boolean | number; @@ -185,11 +192,11 @@ declare module "flexsearch" { term: string, term_index: number, partial: string, - partial_index: number + partial_index: number, ) => number; // Persistent-specific options - db?: StorageInterface; + db?: D; commit?: boolean; // Language-specific Options and Encoding @@ -213,7 +220,7 @@ declare module "flexsearch" { type DefaultSearchResults = Id[]; type IntermediateSearchResults = Array; - type SearchResults = DefaultSearchResults | Resolver; + type SearchResults = R extends true ? Resolver : DefaultSearchResults; /** * **Document:** @@ -222,73 +229,100 @@ declare module "flexsearch" { * * Usage: https://github.com/nextapps-de/flexsearch#usage */ - export class Index { - constructor(options?: Preset | IndexOptions); + type IndexSearchResultsWrapper = + W extends false ? D extends undefined ? SearchResults : Promise> : Promise> + + export class Index { + constructor(options?: Preset | IndexOptions); + add(id: Id, content: string): this | Promise; + /** * @deprecated The method "append" will be removed in an upcoming release, just use "add" instead */ append(id: Id, content: string): this | Promise; + update(id: Id, content: string): this | Promise; + remove(id: Id): this | Promise; - search(query: string, options?: Limit | SearchOptions): SearchResults | Promise; - search(query: string, limit: number, options: SearchOptions): SearchResults | Promise; - search(options: SearchOptions): SearchResults | Promise; - searchCache(query: string, options?: Limit | SearchOptions): SearchResults | Promise; - searchCache(query: string, limit: number, options: SearchOptions): SearchResults | Promise; - searchCache(options: SearchOptions): SearchResults | Promise; + + search(query: string, limit?: Limit): IndexSearchResultsWrapper; + search(query: string, options?: SearchOptions): IndexSearchResultsWrapper; + search(query: string, limit: Limit, options: SearchOptions): IndexSearchResultsWrapper; + search(options: SearchOptions): IndexSearchResultsWrapper; + + searchCache(query: string, limit?: Limit): W extends false ? SearchResults : Promise; + searchCache(query: string, options?: Limit | SearchOptions): IndexSearchResultsWrapper; + searchCache(query: string, limit: Limit, options: SearchOptions): IndexSearchResultsWrapper; + searchCache(options: SearchOptions): IndexSearchResultsWrapper; + // https://github.com/nextapps-de/flexsearch#check-existence-of-already-indexed-ids contain(id: Id): boolean | Promise; + clear(): void | Promise; + cleanup(): void | Promise; // Export and Import export(handler: ExportHandler): void; export(handler: ExportHandlerAsync): Promise; + import(key: string, data: string): void; + serialize(with_function_wrapper?: boolean): SerializedFunctionString; // Persistent Index mount(db: StorageInterface): Promise; + commit(replace_all_contents?: boolean): Promise; + destroy(): Promise; // Async Methods addAsync( id: Id, content: string, - callback?: AsyncCallback + callback?: AsyncCallback, ): Promise; + /** @deprecated The method "append" will be removed in an upcoming release, just use "add" instead */ appendAsync( id: Id, content: string, - callback?: AsyncCallback + callback?: AsyncCallback, ): Promise; + updateAsync( id: Id, content: string, - callback?: AsyncCallback + callback?: AsyncCallback, ): Promise; + removeAsync( id: Id, - callback?: AsyncCallback + callback?: AsyncCallback, ): Promise; + searchAsync( query: string, - options?: Limit | SearchOptions, - callback?: AsyncCallback + limit?: Limit, + callback?: AsyncCallback, ): Promise - searchAsync( + searchAsync( + query: string, + options?: SearchOptions, + callback?: AsyncCallback>, + ): Promise> + searchAsync( query: string, limit: Limit, - options?: SearchOptions, - callback?: AsyncCallback - ): Promise; - searchAsync( - options: SearchOptions, - callback?: AsyncCallback - ): Promise; + options?: SearchOptions, + callback?: AsyncCallback>, + ): Promise>; + searchAsync( + options: SearchOptions, + callback?: AsyncCallback>, + ): Promise>; } /** @@ -298,9 +332,11 @@ declare module "flexsearch" { * * Worker index: https://github.com/nextapps-de/flexsearch#worker-index */ - export class Worker extends Index { + export class Worker extends Index { constructor(options?: Preset | WorkerIndexOptions); + export(): Promise; + import(): Promise; } @@ -308,15 +344,15 @@ declare module "flexsearch" { /* Document Search */ /************************************/ - type FieldOptions = IndexOptions & { - field: FieldName, + type FieldOptions = IndexOptions & { + field: FieldName, filter?: (content: string) => boolean; custom?: (content: string) => string; config?: WorkerConfigURL | WorkerConfigPath; }; - type TagOptions = { - field: FieldName; + type TagOptions = { + field: FieldName; filter?: (content: string) => boolean; custom?: (content: string) => string; db?: StorageInterface; @@ -334,40 +370,47 @@ declare module "flexsearch" { * * Document options: https://github.com/nextapps-de/flexsearch#document-options */ - type DocumentOptions = IndexOptions & { - worker?: boolean | WorkerURL | WorkerPath; - doc?: DocumentDescriptor; - document?: DocumentDescriptor; + type WorkerType = boolean | WorkerURL | WorkerPath + + type DocumentOptions = + IndexOptions + & { + worker?: W; + doc?: DocumentDescriptor; + document?: DocumentDescriptor; }; - type DefaultDocumentSearchResults = Array<{ - field?: FieldName; - tag?: FieldName; + export type DefaultDocumentSearchResults = Array<{ + field?: FieldName; + tag?: FieldName; result: DefaultSearchResults; }>; - type EnrichedDocumentSearchResults = Array<{ - field?: FieldName; - tag?: FieldName; + export type EnrichedDocumentSearchResults = Array<{ + field?: FieldName; + tag?: FieldName; result: Array<{ id: Id; - doc: DocumentData | null; + doc: D | null; highlight?: string; }>; }>; - type MergedDocumentSearchResults = Array<{ + export type MergedDocumentSearchResults = Array<{ id: Id; - doc: DocumentData | null; + doc: D | null; field?: FieldName[]; tag?: FieldName[]; }>; - type DocumentSearchResults = - DefaultDocumentSearchResults | - EnrichedDocumentSearchResults | - MergedDocumentSearchResults | - Resolver; + type DocumentSearchResults = + R extends true ? Resolver : + M extends true ? + MergedDocumentSearchResults : + E extends true ? + EnrichedDocumentSearchResults : + DefaultDocumentSearchResults + /** * # Document Search Result @@ -384,14 +427,16 @@ declare module "flexsearch" { * * Document search options: https://github.com/nextapps-de/flexsearch#document-search-options */ - type DocumentSearchOptions = SearchOptions & { + type DocumentSearchOptions = + SearchOptions + & { tag?: Object | Array; - field?: Array | DocumentSearchOptions | string[] | string; - index?: Array | DocumentSearchOptions | string[] | string; - pluck?: FieldName | DocumentSearchOptions; + field?: Array> | DocumentSearchOptions | string[] | string; + index?: Array> | DocumentSearchOptions | string[] | string; + pluck?: FieldName | DocumentSearchOptions; highlight?: HighlightOptions | TemplateResultHighlighting; - enrich?: boolean; - merge?: boolean; + enrich?: E; + merge?: M; }; type DocumentValue = @@ -405,103 +450,153 @@ declare module "flexsearch" { [key: string]: DocumentValue | DocumentValue[]; }; + type DocumentSearchResultsWrapper< + D extends DocumentData = DocumentData, + W extends WorkerType = false, + B extends StorageInterface = undefined, + R extends boolean = false, + E extends boolean = false, + M extends boolean = false, + > = W extends false + ? B extends undefined + ? DocumentSearchResults + : Promise> + : Promise> + /** * **Document:** * * Basic usage and variants: https://github.com/nextapps-de/flexsearch#basic-usage-and-variants * * API overview: https://github.com/nextapps-de/flexsearch#api-overview * * Document store: https://github.com/nextapps-de/flexsearch#document-store */ - export class Document { - constructor(options: DocumentOptions); + export class Document { + constructor(options: DocumentOptions); + + add(id: Id, document: D): this | Promise; + add(document: D): this | Promise; - add(id: Id, document: DocumentData): this | Promise; - add(document: DocumentData): this | Promise; /** @deprecated The method "append" will be removed in an upcoming release, just use "add" instead */ - append(id: Id, document: DocumentData): this | Promise; + append(id: Id, document: D): this | Promise; /** @deprecated The method "append" will be removed in an upcoming release, just use "add" instead */ - append(document: DocumentData): this | Promise; - update(id: Id, document: DocumentData): this | Promise; - update(document: DocumentData): this | Promise; + append(document: D): this | Promise; + + update(id: Id, document: D): this | Promise; + update(document: D): this | Promise; + remove(id: Id): this | Promise; - remove(document: DocumentData): this | Promise; + remove(document: D): this | Promise; + // https://github.com/nextapps-de/flexsearch#field-search - search(query: string, options?: Limit | DocumentSearchOptions): DocumentSearchResults | Promise; - search(query: string, limit: number, options: DocumentSearchOptions): DocumentSearchResults | Promise; - search(options: DocumentSearchOptions): DocumentSearchResults | Promise; - searchCache(query: string, options?: Limit | DocumentSearchOptions): DocumentSearchResults | Promise; - searchCache(query: string, limit: number, options: DocumentSearchOptions): DocumentSearchResults | Promise; - searchCache(options: DocumentSearchOptions): DocumentSearchResults | Promise; + search(query: string, limit: Limit): DocumentSearchResultsWrapper; + search( + query: string, + options?: DocumentSearchOptions, + ): DocumentSearchResultsWrapper; + search( + query: string, + limit: Limit, + options: DocumentSearchOptions, + ): DocumentSearchResultsWrapper; + search( + options: DocumentSearchOptions, + ): DocumentSearchResultsWrapper; + + searchCache(query: string, limit: Limit): W extends false ? DocumentSearchResults : Promise>; + searchCache( + query: string, + options?: DocumentSearchOptions, + ): DocumentSearchResultsWrapper; + searchCache( + query: string, + limit: Limit, options: DocumentSearchOptions, + ): DocumentSearchResultsWrapper; + searchCache( + options: DocumentSearchOptions, + ): DocumentSearchResultsWrapper; + // https://github.com/nextapps-de/flexsearch#check-existence-of-already-indexed-ids contain(id: Id): boolean | Promise; + clear(): void | Promise; + cleanup(): void | Promise; - get(id: Id): Promise | DocumentData | null; - set(id: Id, document: DocumentData): this; - set(document: DocumentData): this; + + get(id: Id): Promise | D | null; + + set(id: Id, document: D): this; + set(document: D): this; // Export and Import export(handler: ExportHandler): void; export(handler: ExportHandlerAsync): Promise; + import(key: string, data: string): void; // Persistent Index mount(db: StorageInterface): Promise; + commit(replace_all_contents?: boolean): Promise; + destroy(): Promise; // Async Methods addAsync( id: Id, - document: DocumentData, - callback?: AsyncCallback + document: D, + callback?: AsyncCallback, ): Promise; addAsync( - document: DocumentData, - callback?: AsyncCallback + document: D, + callback?: AsyncCallback, ): Promise; + /** @deprecated The method "append" will be removed in an upcoming release, just use "add" instead */ appendAsync( id: Id, - document: DocumentData, - callback?: AsyncCallback + document: D, + callback?: AsyncCallback, ): Promise; /** @deprecated The method "append" will be removed in an upcoming release, just use "add" instead */ appendAsync( - document: DocumentData, - callback?: AsyncCallback + document: D, + callback?: AsyncCallback, ): Promise; + updateAsync( id: Id, - document: DocumentData, - callback?: AsyncCallback + document: D, + callback?: AsyncCallback, ): Promise; updateAsync( - document: DocumentData, - callback?: AsyncCallback + document: D, + callback?: AsyncCallback, ): Promise; + removeAsync( id: Id, - callback?: AsyncCallback + callback?: AsyncCallback, ): Promise; removeAsync( - document: DocumentData, - callback?: AsyncCallback + document: D, + callback?: AsyncCallback, ): Promise; - searchAsync( + + searchAsync(query: string, limit?: Limit): Promise> + searchAsync( query: string, - options?: Limit | DocumentSearchOptions, - callback?: AsyncCallback - ): Promise - searchAsync( + options?: DocumentSearchOptions, + callback?: AsyncCallback>, + ): Promise> + searchAsync( query: string, - limit: number, - options?: DocumentSearchOptions, - callback?: AsyncCallback - ): Promise; - searchAsync( - options: DocumentSearchOptions, - callback?: AsyncCallback - ): Promise; + limit: Limit, + options?: DocumentSearchOptions, + callback?: AsyncCallback>, + ): Promise>; + searchAsync( + options: DocumentSearchOptions, + callback?: AsyncCallback>, + ): Promise>; } type IdType = @@ -577,42 +672,64 @@ declare module "flexsearch" { export class Encoder { constructor(options?: EncoderOptions); + assign(options: EncoderOptions): this; + encode(content: string): string[]; + addMapper(char_match: string, char_replace: string): this; + addMatcher(match: string, replace: string): this; + addStemmer(match: string, replace: string): this; + addFilter(term: string): this; + addReplacer(match: string | RegExp, replace: string): this; } export class Resolver { - constructor(options?: ResolverOptions | IntermediateSearchResults); result: IntermediateSearchResults; + + constructor(options?: ResolverOptions | IntermediateSearchResults); + and(options: ResolverOptions): this; + or(options: ResolverOptions): this; + xor(options: ResolverOptions): this; + not(options: ResolverOptions): this; + limit(limit: number): this; + offset(offset: number): this; + boost(boost: number): this; + resolve(options?: DefaultResolve): SearchResults; } - global{ - class StorageInterface { - constructor(name: string, config: PersistentOptions); - constructor(config: string | PersistentOptions); - mount(index: Index | Document) : Promise; - open() : Promise; - close() : Promise; - destroy() : Promise; - clear() : Promise; + export class StorageInterface { db: any; - }} - export class IndexedDB extends StorageInterface{ - db: IDBDatabase + constructor(name: string, config: PersistentOptions); + + constructor(config: string | PersistentOptions); + + mount(index: Index | Document): Promise; + + open(): Promise; + + close(): Promise; + + destroy(): Promise; + + clear(): Promise; + } + + export class IndexedDB extends StorageInterface { + db: IDBDatabase; } const FlexSearch: { @@ -623,7 +740,7 @@ declare module "flexsearch" { Charset: typeof Charset, Resolver: typeof Resolver, IndexedDB: typeof IndexedDB - } + }; export default FlexSearch; } diff --git a/test/types.ts b/test/types.ts new file mode 100644 index 0000000..3d93bb5 --- /dev/null +++ b/test/types.ts @@ -0,0 +1,70 @@ +import { + DefaultDocumentSearchResults, + Document, + Index, + Worker, + Resolver, + StorageInterface, +} from "flexsearch"; +import "../index"; + +async function test() { + const document = new Document<{ + title: string + description: string + tags: { + name: string + id: number + }[] + }>({ + document: { + index: [ "tags" ], + }, + }); + // The correct type + const doc1 = await document.searchAsync({}); + const doc2: Resolver = await document.searchAsync({ + resolve: true, + }); + const doc3 = await document.searchAsync({ + enrich: true, + }); + const doc4 = await document.searchAsync({ + enrich: true, + merge: true, + }); + doc4[0].doc.title; + // The wrong type + // @ts-expect-error + const docw1: Resolver = await document.searchAsync({}); + // @ts-expect-error + const docw2: DefaultDocumentSearchResults = await document.searchAsync({ + enrich: true, + }); + // Promise? + const documentNoWorker = new Document({}); + const doc5 = documentNoWorker.search({}); // No Promise + + const documentWorker = new Document({ + worker: true, + }); + const doc6 = await documentWorker.search({}) // Promise + + const documentWorker2 = new Document({ + worker: '...', + }); + const doc7 = await documentWorker2.search({}) // Promise + + const index = new Index({}) + const idx = index.search({}) // No Promise + + const worker = new Worker() + const wkr = await worker.search({}) // Promise + + const documentDb = new Document({ + db: {} as unknown as StorageInterface + }) + + const doc8 = documentDb.search({}) // Promise +} + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..678ff85 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "target": "ESNext" + } +}