1.1.2 • Published 7 months ago

dyna-switch v1.1.2

Weekly downloads
2
License
MIT
Repository
github
Last release
7 months ago

dynaSwitch

dynaSwitch combines the benefits of JavaScript's switch statement and allows inline value returns.

How to use JavaScript's switch statement for inline value

Suppose we have the following themes:

interface ITheme {
  color: string;
  backgroundColor: string;
}

const lightTheme: ITheme = {color: 'black', backgroundColor: 'white'};
const darkTheme: ITheme = {color: 'white', backgroundColor: 'black'};
const redTheme: ITheme = {color: 'red', backgroundColor: 'white'};

We can write the following code to obtain the appropriate theme based on a string value:

const themeName: string = getUIThemeName();

const theme: ITheme = (() => {
  switch (themeName) {      // The value that is going to be tested
    case 'light':           // The cases
      return lightTheme;
    case 'dark':
      return darkTheme;
    case 'red':
      return redTheme;
    default:
      return lightTheme;    // Default value
  }
})();

Introducing dynaSwitch

With dynaSwitch, we can achieve the same result in a more concise manner:

const themeName: string = getUIThemeName();

const result = dynaSwitch<ITheme>(
  themeName,                // The value that is going to be tested    
  lightTheme,               // Default value
  {                         // Cases (as object)
    light: lightTheme,
    dark: darkTheme,
    red: () => redTheme,    // The value would be returned by a function also
  },
);

Benefits

  • Organized like JavaScript's switch statement
  • Less code, more readable
  • Prevents accidental fall-through cases
  • Always returns a value (the second argument serves as the default value)
  • Provides type safety in TypeScript, ensuring the expected value is returned
  • The cases dictionary argument can be reused and can handle more complex objects.

Using dynaSwitch with functions

In this example, we utilize functions instead of values in the dynaSwitch cases. This allows us to have variables with the same names inside the functions, something not possible with the traditional switch statement unless closures are used. This feature is particularly useful in Redux reducers, where the cases often require the use of the same variable names.

const themeName: string = getUIThemeName();

const result = dynaSwitch<ITheme>(
  themeName,                // The value that is going to be tested    
  lightTheme,               // Default value
  {                         // Dictionary with the cases
    light: () => {
      const isUserAdmin: boolean = getUserAdmin();
      const theme = { ...lightTheme };
      theme.profile.color = 'red';
      return theme;
    },
    dark: () => {
      const isUserAdmin: boolean = getUserAdmin();
      const theme = { ...darkTheme };
      theme.profile.color = 'maroon';
      return theme;
    },
    red: () => {
      const isUserAdmin: boolean = getUserAdmin();
      const theme = { ...redTheme };
      theme.profile.color = 'pink';
      return theme;
    },
  },
);

Introducing dynaSwitchEnum

With dynaSwitchEnum, you can easily achieve super type-safe values for each option of an Enum, thanks to TypeScript's internal Record feature. This means that if the Enum is modified in the future, you will receive TypeScript errors during compilation for any missing or modified Enum options.

enum ETheme {
  DARK = "DARK",
  LIGHT = "LIGHT",
  REDISH = "REDISH",
}

interface ITheme {
  color: string;
  backgroundColor: string;
}

const themeSetups: Record<ETheme, ITheme> = {
  [ETheme.LIGHT]: {
    color: 'black', backgroundColor: 'white',
  },
  [ETheme.DARK]: {
    color: 'white', backgroundColor: 'black',
  },
  [ETheme.REDISH]: {
    color: 'red', backgroundColor: 'white',
  },
};

dynaSwitchEnum<ETheme, ITheme>(ETheme.LIGHT, themeSetups).color; // returns "black"

dynaSwitchEnum<ETheme, ITheme>("something", themeSetups).color; // not compilable, due to TS!

Alternatively, you can define the dictionary directly. This is especially helpful when you want to convert an enum to a React component prop.

dynaSwitchEnum<ETheme, string>(
  ETheme.REDISH,
  {
    [ETheme.LIGHT]: 'Light',
    [ETheme.DARK]: 'Darky',
    [ETheme.REDISH]: 'Redish',
  },
); // This returns "Redish"!

Benefits

  • Provides super type safety for both the Enum and the returned value
  • Always returns a value

Introducing dynaSwitchIf

dynaSwitchIf is similar to dynaSwitch, but instead of using a dictionary, it employs an array of "if" statements. The first valid "if" statement is executed. This approach makes it easier to assign values compared to using keys in a dictionary.

This dynaSwitch implementation works like an "if-else" statement.

Example:

const themeName = 'dark';

const theme = dynaSwitchIf<ITheme>(
  themeName,                                // The value that is going to be tested
  lightTheme,                               // Default value
  [
    {if: 'light', then: lightTheme},
    {if: 'dark', then: () => darkTheme},    // The `then` would be a function
    {if: 'red', then: redTheme},
  ],
);

expect(theme.color).toBe('white');

API

TypeScript signatures of the methods:

dynaSwitch

export interface IDynaSwitchCasesDic<TResult = any> {
  [enumCase: string]: TResult | (() => TResult);
}

export const dynaSwitch = <TResult = any, TTestValue = string | number>(
  testValue: TTestValue,
  defaultValue: TResult | (() => TResult),
  cases: IDynaSwitchCasesDic<TResult>,
) => TResult;

dynaSwitchEnum

export const dynaSwitchEnum = <TEnum extends string | number | symbol, TResult>(
  testValue: TEnum,
  cases: Record<TEnum, TResult>,
) => TResult;

dynaSwitchIf

export type TDynamicValue<T = any> = T | (() => T);

export const dynaSwitchIf = <TResult = any, TTestValue = any>(
  testValue: TTestValue,
  defaultValue: TDynamicValue<TResult>,
  cases: { if: TDynamicValue<TTestValue>; then: TDynamicValue<TResult> }[],
) => TResult;