// Type definitions for Lowdb 1.0.0 // Project: https://github.com/typicode/lowdb // Definitions by: typicode // Bazyli Brzóska // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.6 import { LoDashExplicitWrapper, LoDashStatic } from "lodash"; declare let Lowdb: Lowdb.lowdb; export = Lowdb; declare namespace Lowdb { export type AdapterOptions = { defaultValue?: SchemaT; serialize?: (data: SchemaT) => string; deserialize?: (serializedData: string) => SchemaT; }; export interface BaseAdapter extends AdapterOptions { readonly "@@reference": SchemaT; new ( source: string, options?: AdapterOptions ): BaseAdapter; source: string; } export interface AdapterSync extends BaseAdapter { new ( source: string, options?: AdapterOptions ): AdapterSync; readonly "@@isAsync": False; write(state: Object): void; } export interface AdapterAsync extends BaseAdapter { new ( source: string, options?: AdapterOptions ): AdapterAsync; readonly "@@isAsync": True; write(state: Object): Promise; } export type Adapter = | AdapterSync | AdapterAsync; export interface LowdbBase< SchemaT extends {}, AdapterT extends Adapter > { read: () => Wrapper; getState: () => SchemaT; setState: (state: SchemaT) => this; } export interface Lowdb> extends LowdbBase, LoDashExplicitWrapper { _: LoDashStatic; /** * @description Be careful: This function overwrites the whole database. */ write(returnValue?: T): Wrapper; } export interface LowdbFp< SchemaT extends {} = any, AdapterT extends Adapter = Adapter > extends LowdbBase { /** * @description Be careful: This function overwrites the whole database. */ write(returnValue?: T): Wrapper; /** * @description Returns a function that allows you to access/modify the database at a given path. * @example * ```js * const posts = db('posts') * const firstPost = posts(all => all[0]) * posts.write((allPosts) => [...allPosts, {title: 'Yup!'}]) * ``` */ ( path: TKey | [TKey], defaultValue?: SchemaT[TKey] ): FpReturn; ( path: [TKey, TSubKey], defaultValue?: SchemaT[TKey][TSubKey] ): FpReturn; < TKey extends keyof SchemaT, TSubKey extends keyof SchemaT[TKey], TSubKey2 extends keyof SchemaT[TKey][TSubKey] >( path: [TKey, TSubKey, TSubKey2], defaultValue?: SchemaT[TKey][TSubKey][TSubKey2] ): FpReturn; < TKey extends keyof SchemaT, TSubKey extends keyof SchemaT[TKey], TSubKey2 extends keyof SchemaT[TKey][TSubKey], TSubKey3 extends keyof SchemaT[TKey][TSubKey][TSubKey2] >( path: [TKey, TSubKey, TSubKey2, TSubKey3], defaultValue?: SchemaT[TKey][TSubKey][TSubKey2][TSubKey3] ): FpReturn; < TKey extends keyof SchemaT, TSubKey extends keyof SchemaT[TKey], TSubKey2 extends keyof SchemaT[TKey][TSubKey], TSubKey3 extends keyof SchemaT[TKey][TSubKey][TSubKey2], TSubKey4 extends keyof SchemaT[TKey][TSubKey][TSubKey2][TSubKey3] >( path: [TKey, TSubKey, TSubKey2, TSubKey3, TSubKey4], defaultValue?: SchemaT[TKey][TSubKey][TSubKey2][TSubKey3][TSubKey4] ): FpReturn; (path: string | Array, defaultValue?: T): T; } export interface FpReturn { /** * Execute a series of functions on the data at a given path. * Result of previous function is the input of the next one. * Returns the result of the last function. */ (f1: (a1: PathT) => R1): R1; // (f1: [(a1: PathT) => R1]): R1; (f1: [(a1: PathT) => R1, (a: R1) => R2]): R2; (f1: [(a1: PathT) => R1, (a: R1) => R2, (a: R2) => R3]): R3; ( f1: [(a1: PathT) => R1, (a: R1) => R2, (a: R2) => R3, (a: R3) => R4] ): R4; ( f1: [ (a1: PathT) => R1, (a: R1) => R2, (a: R2) => R3, (a: R3) => R4, (a: R4) => R5 ] ): R5; ( f1: [ (a1: PathT) => R1, (a: R1) => R2, (a: R2) => R3, (a: R3) => R4, (a: R4) => R5, (a: R5) => R6 ] ): R6; ( f1: [ (a1: PathT) => R1, (a: R1) => R2, (a: R2) => R3, (a: R3) => R4, (a: R4) => R5, (a: R5) => R6, (a: R6) => R7 ] ): R7; (funcs: Array<(a: any) => any>): any; /** * @description Writes the change to the database, based on the callback's return value. * @example * ```js * posts.write((allPosts) => [...allPosts, {title: 'Yup!'}]) * ``` */ write(f1: (a1: PathT) => R1): Wrapper; } export type lowdb = < SchemaT extends AdapterT[ReferenceProperty], AdapterT extends Adapter >( adapter: AdapterT ) => If< AdapterT[AsyncProperty], Promise, AdapterT>>, Lowdb, AdapterT> >; export type lowdbFp = < SchemaT extends AdapterT[ReferenceProperty], AdapterT extends Adapter >( adapter: AdapterT ) => If< AdapterT[AsyncProperty], Promise>, LowdbFp >; export type Wrapper = WrapInPromiseIfTrue< T, AdapterT[AsyncProperty] >; } /** * lodash augmentation is necessary in order not to duplicate the whole lodash definition * it's mostly harmless, aside from the fact that 'write' becomes a method in a lodash.chain() */ declare module "lodash" { interface LoDashExplicitWrapper { write(): WrapInPromiseIfAsyncTag>; // override lodash's methods only if source tag is present: value(): GetReferenceTypeIfDefined; } } // utility types: type WrapInPromiseIfTrue = If, T>; type ReferenceProperty = "@@reference"; /** * Hidden source property for resolving to the correct type. * It is doubly nested so that we retain all nullability information */ type SourceReference = { readonly "@@reference"?: { readonly "@@reference": T }; }; type GetReferenceParent> = NonNull< U[keyof U & ReferenceProperty] >; type GetDefinedReferenceType< TValue, SourceParent = GetReferenceParent > = SourceParent[keyof SourceParent & ReferenceProperty]; type AsyncProperty = "@@isAsync"; type AsyncTag = { readonly "@@reference"?: { readonly "@@isAsync"?: true } }; type SyncTag = {}; type HasAsyncTag> = If< HasReferenceProperty, UnionHasKey, False >; type HasReferenceProperty = UnionHasKey, ReferenceProperty>; type WrapInPromiseIfAsyncTag< TaggedT, WrappedT = GetReferenceTypeIfDefined > = WrapInPromiseIfTrue>; /** * Resolves the hidden source type * while preserving nullability as much as possible */ type GetReferenceType> = If< IsEmptyType, Source | undefined, Source >; type GetReferenceTypeIfDefined = If< HasReferenceProperty, GetReferenceType, T >; type RecursivelyExtend = If< IsArrayType, MapArrayType, AddT>, MapScalarOrObjectType > & SourceReference; type MapScalarOrObjectType = If< IsScalarOrInstance, T, MapObjectWithPossibleArrays & SourceReference & AddT >; type MapObjectWithPossibleArrays = { [P in keyof T]: If< IsArrayType, MapArrayType, AddT> & SourceReference, MapScalarOrObjectType > }; type MapArrayType, AddT, T = Arr[-1]> = Array< MapObjectWithPossibleArrays & SourceReference & AddT > & AddT; ////////////////////////////////////////// // generic utils (some from https://github.com/tycho01/typical/): type False = "0"; type True = "1"; type Bool = True | False; type If = { 1: Then; 0: Else }[Cond]; type Obj = { [k: string]: T }; type And = ({ 1: { 1: "1" } & Obj<"0"> } & Obj< Obj<"0"> >)[A][B]; type UnionHasKey = ({ [S in Union]: "1" } & Obj<"0">)[K]; type Indeterminate = And< UnionHasKey, UnionHasKey >; type Not = { "1": "0"; "0": "1" }[T]; type Determinate = Not>; type DefinitelyYes = And>; type UnionContained = DefinitelyYes< ({ [P in U]: "1" } & Obj<"0">)[T | U] >; type UnionEmpty = And< UnionContained, UnionContained >; type UnionToObject = { [K in Keys]: K }; type Keyed = { [K in keyof T]: K }; type KeyedSafe = Keyed & Obj; type IntersectionUnions = KeyedSafe< UnionToObject >[Big]; type UnionsOverlap = Not< UnionEmpty> >; type DiffUnion = ({ [P in T]: P } & { [P in U]: never } & { [k: string]: never })[T]; type ObjectHasKey = UnionHasKey; type ArrayPrototypeProperties = DiffUnion< keyof Array, "toString" | "toLocaleString" >; type IsArrayType = DefinitelyYes>; /** * either: undefined, never, null or {} * can also be used to check if one union contains one of the above **/ type IsEmptyType = UnionEmpty; /** * false for: undefined, interfaces * true for: Array, boolean, number, string, Object */ type IsScalarOrInstance = ObjectHasKeySafe; type ObjectHasKeySafe = UnionsOverlap; type NonNull = T & {}; interface List { readonly [n: number]: T; }