Wondering what’s next for npm?Check out our public roadmap! »

    fully-optional
    TypeScript icon, indicating that this package has built-in type declarations

    1.2.0 • Public • Published

    fully-optional

    npm package Build Status Coverage Status dependencies Status Code style

    Handle null and undefined in safe and composable way.

    Reasoning

    When dealing with nullability in Typescript you need to create a union type T | undefined and then rely on Typescript control flow analysis to make sure you don't use undefined where you want something else

    However this approach has its flaws:

    • The code is imperative, you do not immediately see the business logic or data manipulation
    • The code can quickly become a mess when you nest if statements or check multiple values for null
    type Data = {
      data?: {
        date?: string;
      };
    };
     
    declare const getData: () => Data | undefined;
     
    declare const parseDate: (date: string) => Date | undefined;

    Default approach

    const f = () => {
      const data = getData();
     
      let date: Date | undefined;
     
      if (data && data.data) {
        const dateString = data.data.date;
     
        if (dateString) {
          date = parseDate(dateString);
        }
      }
     
      date = date || new Date();
     
      return date.toLocaleDateString();
    };

    Using fully-optional

    import { flow } from 'lodash/fp';
    import { bind, withDefaultLazy } from 'fully-optional';
     
    const f = flow(
      getData,
      bind((data) => data.data),
      bind((data) => data.date),
      bind(parseDate),
      withDefaultLazy(() => new Date()),
      (date) => date.toLocaleDateString(),
    );

    This library provides an abstraction over manual handling of null values with a concise and composable API

    There are, however, other approaches to solve null problem with composability in mind: Maybe or Option monads.

    So why should you use fully-optional instead of a Maybe monad?

    • When using Maybes you introduce a new wrapper datatype that does not integrate well into an existing javascript ecosystem

    • There is no need to introduce a new way to handle null values when we have a good standart solution with union types

    • Union types scale better than Maybe monad.

      For example you have a function

      declare const f: (value: number) => Maybe<T>;

      When you refactor it's type to

      declare const f: (value: number) => T;

      You will break all the callers of this function, but you will not break them by changing the return type from T | undefined to T

    Install

    npm install fully-optional
    yarn add fully-optional

    Usage

    import { bind } from 'fully-optional';
     
    declare const a: string | undefined;
     
    bind(a, parseInt); // inferred type number | undefined

    All functions are also curried data-last to allow composition

    import { flow } from 'lodash/fp';
    import { bind } from 'fully-optional';
     
    type X = {
      a?: {
        b?: string;
      };
    };
     
    declare const f: (...args: any[]) => X | undefined;
     
    const r = flow(
      f,
      bind((e) => e.a),
      bind((e) => e.b),
      bind(parseInt),
    ); // inferred type number | undefined

    API

    all

    Apply a function to an array of values if all of them are not null or undefined

    declare const arr: [string | undefined, number | undefined];
     
    all(arr, ([s, n]) => parseInt(s) * n); // number | undefined

    bind

    Apply a function to a value if it is not null or undefined

    declare const a: string | undefined;
     
    bind(a, (e) => e.toUpperCase()); // string | undefined

    isEmpty

    Check if value is null or undefined

    isEmpty(a);

    isNotEmpty

    Check if value is not null or undefined

    isNotEmpty(a);

    match

    Give two functions to handle both empty and non empty cases

    declare const a: string | undefined;
     
    match(a, {
      some: (e) => e.toUpperCase(),
      none: () => '',
    });

    withDefault

    Return default value if the argument is null or undefined

    declare const a: string | undefined;
     
    withDefault(a, '');

    withDefaultLazy

    Calculate and return default value if the argument is null or undefined

    declare const a: string | undefined;
    declare const expensiveDefaultValue: () => string;
     
    withDefaultLazy(a, expensiveDefaultValue);

    Contributing

    Pull requests are welcome.
    Please make sure to update tests as appropriate.

    License

    MIT

    Install

    npm i fully-optional

    DownloadsWeekly Downloads

    3

    Version

    1.2.0

    License

    MIT

    Unpacked Size

    25 kB

    Total Files

    53

    Last publish

    Collaborators

    • avatar