Как избежать множественных проверок if-else (кода спагетти) в машинописи - PullRequest
0 голосов
/ 21 февраля 2020

Общий вопрос : Как избежать уродливого кода if-else, когда вам приходится иметь дело с несколькими проверками, основанными на некотором типе строки?

Пример : У меня есть объект «Задача» в приложении TODO, который может быть разных типов, например, простой флажок, текстовое поле, дата, комбинация полей и т. Д. c. Для каждого типа «Задачи» существует различная логика c о том, как изменить объект перед его сохранением.

Как использовать машинопись и упорядочить код без операторов if-else.

1 Ответ

1 голос
/ 21 февраля 2020

Вот мое решение проблемы.

Давайте определим некоторые базовые c машинописные интерфейсы.

interface TaskType {
  title: string;
  type: TypeEnum;
  metadata: Record<string, any>; // it stores different kind of objects based on the type
}

enum TypeEnum {
  CHECKBOX = 'CHECKBOX',
  TEXTAREA = 'TEXTAREA',
  DATE = 'DATE',
  WHATEVER = 'WHATEVER',
}

Давайте определим интерфейс для функции, которая отвечает за обновление задачи на основе некоторых данных формы

// this is what you expect to get from HTML form
export interface TaskFormType {
  metadata: Record<string, any>; // you can define it more strictly according to your needs
}

// interface for function arguments
export interface SaveTaskFnArgs {
  task: TaskType;
  formData: TaskFormType;
}

// interface for the function which should prepare a new version of the task
export interface SaveTaskFn {
  (args: SaveTaskFnArgs): TaskType;
}

Теперь мы можем определить выделенную функцию SaveTaskFn для каждого типа Task

const checkboxSave: SaveTaskFn = ({ task, formData }) => {
  const updatedTask: TaskType = ... // do what you need to update the task
  return updatedTask;
};
const textareaSave: SaveTaskFn = ({ task, formData }) => { ... }
const dateSave: SaveTaskFn = (args) => { ... }
const whateverSave: SaveTaskFn = (args) => { ... }

Теперь это самое интересное, как на самом деле избежать операторов if-else. 1. Определим отображение между типом задачи и функцией 2. Определим функцию publi c saveTaskFn (поэтому мы можем экспортировать только эту функцию на самом деле)

// 1
const SAVE_TASK_MAPPING: Record<TypeEnum, SaveTaskFn> = {
  [TypeEnum.CHECKBOX]: checkboxSave,
  [TypeEnum.TEXTAREA]: textareaSave,
  [TypeEnum.DATE]: dateSave,
  [TypeEnum.WHATEVER]: whateverSave,
};

// 2

export const saveTask = (args: SaveTaskFnArgs): TaskType => {
  // all if-else statements collapsed in two lines of code (literally);
  const fn: SaveTaskFn = SAVE_TASK_MAPPING[args.task.template.type]; 
  return (fn && fn(args)) || args.task; // here instead of returning original task ( || args.task) you can raise exception or return some default object based on your architecture
};

Вот еще одно преимущество использования таких отображение. Если вы расширите значение TypeEnum в Task новым значением, например, «FILE», вы получите ошибку при наборе текста

Error:(<line>, <column>) TS2741: 
Property 'FILE' is missing in type '{ 
  [TypeEnum.CHECKBOX]: SaveTaskFn; 
  [TypeEnum.TEXTAREA]: SaveTaskFn; 
  [TypeEnum.DATE]: SaveTaskFn; 
  [TypeEnum.WHATEVER]: SaveTaskFn; 
  [TypeEnum.FILE]: SaveTaskFn; 
}' but required in type 'Record<TypeEnum, SaveTaskFn>'.

..., поэтому у вас есть напоминание о реализации новой функции, например fileSave () и добавьте его в отображение следующим образом

const SAVE_TASK_MAPPING: Record<TypeEnum, SaveTaskFn> = {
   ...
  [TypeEnum.FILE]: fileSave,
};

...