UNPKG

@focuson/lens

Version:

A simple implementation of lens using type script

230 lines (228 loc) 14.7 kB
import { NameAnd } from "@focuson/utils"; export declare const identityOptics: <State>(description?: string) => Iso<State, State>; export interface GetOptioner<Main, Child> { getOption: (m: Main) => Child | undefined; } export interface SetOptioner<Main, Child> { setOption: (m: Main, c: Child) => Main | undefined; } export interface Getter<Main, Child> { get: (m: Main) => Child; } export interface Setter<Main, Child> { set: (m: Main, c: Child) => Main; } export interface HasOptional<Main, T> { optional: Optional<Main, T>; } /** An Optional is like a lens, except that it is not guaranteed to 'work'. Specifically if you ask for a child... maybe that child isn't there. * * This is great for things like 'optional values' which are often written as 'name?: type' in typescript. * * It is rare that you create one directly. Usually it is created using 'focusQuery' on a lens */ export declare class Optional<Main, Child> implements GetOptioner<Main, Child>, SetOptioner<Main, Child> { getOption: (m: Main) => Child | undefined; setOption: (m: Main, c: Child) => Main | undefined; description: string; constructor(getOption: (m: Main) => (Child | undefined), optionalSet: (m: Main, c: Child) => Main | undefined, description?: string); set: (m: Main, c: Child) => Main; map: (m: Main, fn: (c: Child | undefined) => Child) => Main; mapDefined: (m: Main, fn: (c: Child) => Child) => Main; /** This is identical to this.setOption(m, undefined) */ clearJson(m: Main): Main; /** Allows us to change the focuson-ed child based on it's existing value * @fn a function that will be given the old value and will calculate the new * @returns a function that given a Main will return a new main with the child transformed as per 'fn' */ transform(fn: (oldChild: Child) => Child): (m: Main) => Main; /** This is used when the 'parameter' points to definite value. i.e. it isn't 'x: X | undefined' or 'x?: X'. If you want to * walk through those you probably want to use 'focusQuery' * * If the type system is complaining and you are sure that it should be OK, check if the previous focusOn should be a focusQuery * @param k */ focusOn<K extends keyof Child>(k: K): Optional<Main, Child[K]>; /** Used to focus onto a child that might not be there. If you don't use this, then the type system is likely to complain if you try and carry on focusing. */ focusQuery<K extends keyof Child, Req extends Required<Child>>(k: K): Optional<Main, Req[K]>; chain<T>(o: Optional<Child, T>): Optional<Main, T>; combine<OtherChild>(other: Optional<Main, OtherChild>): Optional<Main, [Child, OtherChild]>; /** If you desire to change the description this will do that. It is rarely called outside the Lens code itself */ withDescription(description: string): Optional<Main, Child>; combineAs<OtherChild, NewChild>(other: Optional<Main, OtherChild>, iso: Iso<[Child, OtherChild], NewChild>): Optional<Main, NewChild>; chainIntoArray(a: string[]): Optional<Main, string>; chainNthFromPath(pathL: Optional<Main, number>): Optional<Main, any>; chainCalc(pathL: Optional<Main, keyof Child>): Optional<Main, Child[keyof Child]>; toString(): string; } export declare function optional<Main, Child>(getOption: (m: Main) => Child | undefined, setOption: (m: Main, c: Child) => Main | undefined, description?: string): Optional<Main, Child>; export declare function identityOptional<Main>(): Optional<Main, Main>; export declare function orUndefined<T>(description?: string): Optional<T | undefined, T>; export declare function castIfOptional<T, T1>(cond: (t: T) => boolean, description?: string): Optional<T, T1>; /** * Creates a lens with two generics. Lens<Main,Child>. Main is the main 'object' that we start with, and Child is the part of Main that the lens is focuson-ed * @param get should be a sideeffect free function that goes from 'Main' to the focuson-ed child. When called it 'gets' the Child from the Main * @param set should be a sideeffect free function that creates a new Main out of an old main and a new child. It returns the old main with the 'focuson-ed' part replaced by the new child * @param description should probably be the string representation of the class 'Main'. If the main object is of type Dragon, this could be the string 'dragon'. * * Usually these are created by code like * * identityOptics<Dragon>().focuson('head') */ export declare function lens<Main, Child>(get: (main: Main) => Child, set: (main: Main, newChild: Child) => Main, description?: string): Lens<Main, Child>; /** This is the class that represents a Lens<Main,Child> which focuses on Child which is a part of the Main */ export declare class Lens<Main, Child> extends Optional<Main, Child> implements Getter<Main, Child>, Setter<Main, Child> { set: (m: Main, c: Child) => Main; get: (m: Main) => Child; constructor(get: (m: Main) => (Child), set: (m: Main, c: Child) => Main, description: string); /** this is the 'normal' focuson. We use it when we know that the result is there. i.e. if we have * * interface AB{ * a: string, * b?: SomeInterface | undefined * } * * In this case focusOn('a') will give us a Lens<AB,string> but focusOn('b') will give a lens<AB, SomeInterface|undefined>. * @param k */ focusOn<K extends keyof Child>(k: K): Lens<Main, Child[K]>; /** interface AB{ * a: string, * b?: SomeInterface | undefined * } * * In this case it would be redundant to have focusWithDefault('a', "someA") because 'a' should never be undefined. * However (if someValue is a SomeInterface) focusWithDfeault('b', someValue) return Lens<AB,SomeInterface> and if we do a get, and b was undefined, we use 'someValue' * @param k */ focusWithDefault<K extends keyof Child, Req extends Required<Child>>(k: K, def: Child[K]): Lens<Main, Req[K]>; chainLens: <T>(o: Lens<Child, T>) => Lens<Main, T>; /** @deprecated */ chainWith: <T>(o: Lens<Child, T>) => Lens<Main, T>; combineLens<OtherChild>(other: Lens<Main, OtherChild>): Lens<Main, [Child, OtherChild]>; /** If you desire to change the description this will do that. It is rarely called outside the Lens code itself */ withDescription(description: string): Lens<Main, Child>; toString(): string; } /** A factory class that allows us to create new Lens. Every method on it is static, so you would never create one of these * * This class will be removed and replaced with just 'plain functions' * */ export declare class Lenses { /** This is a the normal way to generate lens. It create a link that goes from Main to itself */ static build<Main>(description: string): Lens<Main, Main>; /** Given a main which is an object, with a field name, this returns a lens that goes from the Main to the contents of the field name */ static field: <Main, K extends keyof Main>(fieldName: K) => Lens<Main, Main[K]>; /** Given a main which is an object, with a field name, this returns a lens that goes from the Main to the contents of the field name */ static identity<M>(description?: string): Iso<M, M>; /**This should no longer be needed. It was in fact the need for this method that drove the rewrite using Optionals/Prisms and Isos. * * Nowadays we can use focusQuery * * It should only be used when we 'know' that a Lens<Main,Child|undefined> is really a Lens<Main,Child>. * @deprecated */ static define<T>(): Lens<T | undefined, T>; /** This returns a lens from an array of T to the last item of the array */ static last<T>(): Lens<T[], T>; /** This returns a lens from an array of T to the next item of the array */ static append<T>(): Lens<T[], T>; /** This returns a lens from an array of T to the nth member of the array */ static nth<T>(n: number): Optional<T[], T>; static chainNthFromOptionalFn<From, To, NewTo>(lens: Optional<From, To>, newToFnL: (f: From) => Optional<To, NewTo>, newFragDescription: string): Optional<From, NewTo>; static chainNthRef<From, T>(lens: Optional<From, T[]>, lookup: (name: string) => any, name: string, description?: string): Optional<From, T>; /** Used to to go from ids to values and back again. Obvious if the mapping isn't one to one there will be loss of data */ static chainLookup<Main, V extends string | number | boolean>(opt: Optional<Main, string>, lookupL: Optional<Main, NameAnd<V>>): Optional<Main, V>; /** Used to to go from ids to values and back again. Obvious if the mapping isn't one to one there will be loss of data */ static chainLookupTable<Main, L, K extends keyof L, V extends keyof L>(opt: Optional<Main, string>, lookupL: Optional<Main, L[]>, idName: K, valueName: V): Optional<Main, L[V]>; static constant: <Main, Child>(value: Child, description?: string) => Lens<Main, Child>; static safeList<T>(): Lens<T[] | undefined, T[]>; static if<Main, T>(cond: Optional<Main, boolean>, trueL: Optional<Main, T>, falseL: Optional<Main, T>): Optional<Main, T>; static condition<Main, Cond, T>(cond: Optional<Main, Cond>, fn: (c: Cond) => boolean, trueL: Optional<Main, T>, falseL: Optional<Main, T>): Optional<Main, T>; static calculatedNth<Main, T>(nL: Optional<Main, number>, opt: Optional<Main, T[]>): Optional<Main, T>; } export type FocusOnPathItem = string | FocusOnPathSimpleItem | FocusOnPathNthItem | FocusOnPathPathItem; export interface FocusOnPathSimpleItem { action: '$last' | '$append'; } export declare function isFOPSingle(f: FocusOnPathItem): f is FocusOnPathSimpleItem; export interface FocusOnPathNthItem { action: '[n]'; n: number; } export declare function isFOPNth(f: FocusOnPathItem): f is FocusOnPathNthItem; export interface FocusOnPathPathItem { action: '[path]'; path: FocusOnPathItem[]; } export declare function isFOPPath(f: FocusOnPathItem): f is FocusOnPathPathItem; export declare function prism<Main, Child>(getOption: (m: Main) => Child | undefined, reverseGet: (c: Child) => Main, description?: string): Prism<Main, Child>; export declare function dirtyPrism<Main, Child>(getOption: (m: Main) => Child | undefined, reverseGet: (c: Child) => Main, description?: string): DirtyPrism<Main, Child>; export declare class DirtyPrism<Main, Child> extends Optional<Main, Child> { reverseGet: (c: Child) => Main; constructor(getOption: (m: Main) => (Child | undefined), reverseGet: (c: Child) => Main, description: string); toString(): string; } export declare class Prism<Main, Child> extends DirtyPrism<Main, Child> { constructor(getOption: (m: Main) => (Child | undefined), reverseGet: (c: Child) => Main, description: string); toString(): string; } export declare function iso<Main, Child>(get: (m: Main) => Child, reverseGet: (c: Child) => Main, description?: string): Iso<Main, Child>; export declare class Iso<Main, Child> extends Lens<Main, Child> { reverseGet: (c: Child) => Main; optional: Optional<Main, Child>; constructor(get: (m: Main) => Child, reverseGet: (c: Child) => Main, description: string); toString(): string; } /** This 'changes' two parts of Main simultaneously. * * @param lens1 This is focused in on a part of main that we want to change * @param lens2 This is focused in on a second part of main that we want to change * @param fn1 Given the old values that lens1 and lens2 are focused on, this gives us a new value for the part of main that lens1 is focused on * @param fn2 Given the old values that lens1 and lens2 are focused on, this gives us a new value for the part of main that lens2 is focused on * @returns a function that given a Main will return a new main with the two functions used to modify the parts of Main that the two lens are focused in on * */ export declare const transformTwoValues: <Main, C1, C2>(lens1: Optional<Main, C1>, lens2: Optional<Main, C2>) => (fn1: (c1: C1, c2: C2) => C1, fn2: (c1: C1, c2: C2) => C2) => (main: Main) => Main; /** This 'changes' two parts of Main simultaneously. * * @param lens1 This is focused in on a part of main that we want to change * @param lens2 This is focused in on a second part of main that we want to change * @param main A value that is to be 'changed' by the method. Changed means that we will make a copy of it with changes * @param c1 The new value for the part that lens1 is focused on * @param c2 The new value for the part that lens2 is focused on * @returns a new main with the parts the two lens are focused on changed by the new values * */ export declare const updateTwoValues: <Main, C1, C2>(lens1: Optional<Main, C1>, lens2: Optional<Main, C2>) => (main: Main, c1: C1, c2: C2) => Main; /** This 'changes' three parts of Main simultaneously. * * @param lens1 This is focused in on a part of main that we want to change * @param lens2 This is focused in on a second part of main that we want to change * @param lens3 This is focused in on a third part of main that we want to change * @param main A value that is to be 'changed' by the method. Changed means that we will make a copy of it with changes * @param c1 The new value for the part that lens1 is focused on * @param c2 The new value for the part that lens2 is focused on * @param c3 The new value for the part that lens3 is focused on * @returns a new main with the parts the three lens are focused on changed by the new values * */ export declare const updateThreeValues: <Main, C1, C2, C3>(lens1: Optional<Main, C1>, lens2: Optional<Main, C2>, lens3: Optional<Main, C3>) => (main: Main, c1: C1, c2: C2, c3: C3) => Main; export declare function nthItem<T>(n: number): Optional<T[], T>; export declare function firstIn2<T1, T2>(): Optional<[T1, T2], T1>; export declare function secondIn2<T1, T2>(): Optional<[T1, T2], T2>; export type Transform<Main, Child> = [Optional<Main, Child>, (c: Child | undefined) => Child]; interface DisplayTransform { opt: string; value: any; } export declare function displayTransformsInState<S>(main: S, txs: Transform<S, any>[]): DisplayTransform[]; export declare function massTransform<Main>(main: Main, ...transforms: Transform<Main, any>[]): Main; export declare function nameLensFn<S, T extends NameAnd<any>>(lens: Optional<S, T>): GetNameFn<S, any>; export declare function asGetNameFn<S, T>(nl: NameAndLens<S>): GetNameFn<S, T>; export type GetNameFn<Main, T> = (name: string) => GetOptioner<Main, T>; export interface NameAndLens<S> { [name: string]: Optional<S, any>; } export interface NameAndLensFn<S> { [name: string]: (o: Iso<S, S>) => Optional<S, any>; } export {};