Разработка с поддержкой DEF с помощью Typescript |

Давайте перейдем к сути, Javascript как язык — красивая горячая фигня…

Скриншот 01.11.2018, 18.19.38.jpg

Я только что погуглил «горячий мусор», и это оказалось связано с javascript…

Ладно-ладно, опустите вилы! Es6 — это фантастический набор итераций языка для создания решений для Интернета, серверов и мобильных платформ. Однако у него все еще есть много проблем, которые возникают то здесь, то там. Динамическая типизация помогает создавать системы с минимальным шаблонным кодом и ускоряет поставку.

Однако по мере роста вашего приложения javascript действительно предлагает вам загнать себя в угол.

Возьмите эту невинную функцию.


function doTheThing(data) {
  return data.subproperty.doTheThing();	
}

Хотя javascript с радостью ожидает этого, фактическое поведение зависит от неформального контракта с любой системой, которая использует эту функцию. Если вам нужно реорганизовать это, не могли бы вы ответить на следующее?

  1. Как выглядит аргумент данных?

Здесь мы видим, что данные должны иметь подсвойство с методом doTheThing(). что значит doTheThing() на самом деле сделать однако? что он возвращает?

  1. Что мы делаем с возвращаемым значением?

Сколько систем полагаются на это возвращаемое значение, чтобы следовать определенной схеме свойств и методов, соответствующих интерфейсу? в данном случае мы делегируем это аргументу данных. Это также добавляет немного косвенности к результирующей системе. Если мы полагаемся на то, что это возвращаемое значение соответствует определенному интерфейсу, то по определению нам нужно просмотреть все экземпляры данных в вызовах, чтобы убедиться, что все они также подчиняются.

  1. всегда ли существует подсвойство для данных?

Скажем, кто-то выполняет несколько несвязанных рефакторингов в одной версии объекта данных и удаляет подсвойство. Мы получим ошибку… во время выполнения; Надеюсь, пока вы развиваетесь. Ошибки во время выполнения имеют свойство разбегаться, как тараканы, и никто не хочет иметь дело с необходимостью отлаживать их на производстве. Это еще одна вещь, на которую вы должны обратить внимание, что может быть сложно, если вы не знаете внутреннего представления doTheThing(data)

Я не знаю о вас, но это очень много для документирования, и это ОДНА ФУНКЦИЯ.

Требуемый тип проверки возрастает в геометрической прогрессии по мере увеличения кодовой базы. к счастью, typescript дает нам инструменты для автоматизации этой задачи, позволяя нам добавлять аннотации типов.

давайте посмотрим на пример машинописного текста.

interface IThingArgs {
  subProperty: {
    	doTheThing: () => string;
    };
}

function doTheThing(data: IThingArgs): number {
  return data.subProperty.doTheThing();
}

Просто взглянув на интерфейс и тип возвращаемого значения, мы получаем всю информацию, необходимую для информирования о том, как данные, поступающие в эту функцию, должны работать, а также о том, что мы можем ожидать от нее.

Предположим, мы пытаемся сделать что-то, что мы не должны делать.

let delegationThingy = {
  subProperty: {
    	doTheThing() {
        	return 5
        }
    }
}

let result = doTheThing(delegationThingy);
console.log(result.length);

Наш исходный код javascript с радостью примет это и будет работать без ошибок. Даже если возврат числа, а не строки, как ошибка, будет зависеть от того, как мы используем результат в последующем коде. В этом случае мы пытаемся вызвать для него длину. Поскольку числа не имеют свойства длины, мы получаем ошибку времени выполнения. Для меня это ужасно, так как теперь мы находимся на милости системы, чтобы выявить ошибку, которая может быть в нескольких стеках от источника проблемы.

С нашим машинописным кодом мы получаем ошибку ограничения типа, информирующую нас о том, что делегацияThingy.subProperty.doTheThing() возвращает число, а не строку, как того требует IThingArgs. С правильными инструментами потенциально дорогостоящая ошибка времени выполнения просто превратилась в строку ошибки, которая появляется, как только вы заканчиваете написание вызывающей ошибку строки кода.

«Подождите секунду, — можете сказать вы, — а что, если нам нужен общий повторно используемый код? Я могу использовать бестиповую версию doTheThing() в нескольких контекстах, где мне нужны разные типы возвращаемого значения, и делегировать их между аргументом и всем, что потребляет результат. результат!» И с этим я соглашусь, исходный код javscript — очень острый нож и более гибкий, чем версия машинописного текста.

Мы можем получить наш пирог и съесть его, используя дженерики.


interface IThingArgs<T> {
  subProperty: {
    	doTheThing: () => T;
    };
}

function doTheThing<T>(data: IThingArgs<T>): T {
  return data.subProperty.doTheThing();
}

<T> указывает, что мы будем делегировать фактический тип, который идет здесь, потребителю этой функции. Именно там мы можем применить ограничение типа, а typscript позаботится о том, чтобы все работало.

let delegationThingy = {
  subProperty: {
    	doTheThing() {
        	return 5
        }
    }
}

let delegationThingy2 = {
  subProperty: {
    	doTheThing() {
        	return "foo"; 
        }
    }
}

let result: number = doTheThing<number>(delegationThingy);
let result2: string = doTheThing<string>(delegationThingy2);
console.log(result + result2.length);

console.log(doTheThing<number>(delegationThingy2).length); //compile error


В последнее время я продвигаю машинописный текст в проектах Redux. По мере того, как проекты Redux становятся больше, я лично изо всех сил пытался вернуться к исходному коду моих действий и редукторов, чтобы узнать, какие аргументы мне нужны для плагина. Кроме того, большая часть моего дерева состояний следует неформально определенной, но очень жесткой структуре, от которой зависит остальная часть моего приложения.

Мне нужны довольно надежные гарантии того, что каждая функция редуктора сохраняет структуру того, как должно выглядеть мое состояние, когда оно конкурировало со своими модификациями.

аналогично нашему предыдущему примеру, мы можем использовать набор интерфейсов, чтобы наложить автоматическую проверку ошибок во время компиляции на нашу кодовую базу.


export interface ITodo {
 content: string;
 completed: boolean;
 subtasks?: ITodo[];
}

export interface ITodoListState {
  tasks: ITodo[];
}

const initialState = {
  tasks: [
  	{
      content: 'first task',
      completed: false,
    }
  ]
}

Когда интерфейс имеет ? в свойстве это означает, что этот тип является необязательным, и средство проверки компиляции не выдаст ошибку, если оно не включено. У него есть дополнительное преимущество, заключающееся в том, что вы будете учитывать значения, которые могут быть неопределенными во время компиляции.

Допустим, у нас есть компонент реакции, который отображает элемент списка дел. Мы могли бы попробовать это в первый раз.


interface ITodoItemProps {
  todo: ITodo;
}

const TodoItem = (props: ITodoItemProps): JSX.Element => {
  return (
    <div>
      <h1
        className={(props.todo.completed) ? 'not-done' : 'done'}>			
          {props.todo.content}
      </h1>
      <ul>
      {
        props.todo.subtasks.map((subtask) => <li> {subtask.content} </li>)
      }
      </ul>
    </div>
  )
}

Это может привести к неприятным ошибкам во время выполнения, потому что подзадачи могут не всегда существовать. Typescript знает, что это необязательный тип, и будет лаять на вас, чтобы вы сначала проверили его на undefined.

Скриншот 01.11.2018, 18:48:56.png

Опять же, компилятор заранее предупреждает нас об использовании свойства, которое может не существовать, и информирует нас о включении кода для проверки этого. Добавление проверки на неопределенность устраняет ошибку и помогает избежать ошибки в вашем коде реакции.

const TodoItem = (props: ITodoItemProps): JSX.Element => {
  return (
    <div>
      <h1
        className={(props.todo.completed) ? 'not-done' : 'done'}>			
          {props.todo.content}
      </h1>
      {
        (props.todo.subtasks) ? (
          <ul>
          {
            props.todo.subtasks.map((subtask) => <li> {subtask.content} </li>)
          }
          </ul>
        ) : null
      }
    </div>
  );
};

Как видите, Typescript дает нам множество инструментов, позволяющих избежать проблем с интерфейсами во время выполнения. Мы даже можем расширять интерфейсы и передавать расширенные типы функциям, ожидающим базовый тип, аналогично расширенным классам.

В редуксе мы отправляем действия редюсеру. не было бы неплохо создать общий тип действия и применить ограничения типа?

interface IAction<T> {
  type: string;
  payload: T
}

export const addTodoAction = (todo: ITodo): IAction<ITodo> => {
  return {
      type: "ADD_TODO",
      payload: todo
    }
};

const addTodoReducer = (state: ITodoListState, action: IAction<ITodo>): ITodoListState => {
    return {
      ...state,
      tasks: state.tasks.concat([action.payload])
    };
}

Теперь мы не только можем проверять аргументы во время компиляции, но и достаточно однозначно определить, что каждая часть этой системы ожидает в качестве входных и выходных данных. если ввод или ожидаемый вывод не совпадают, typecipt сообщит пользователю задолго до того, как он увидит манифест ошибки времени выполнения.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *