Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CodeFest 2018. Михаил Шатихин (СКБ Контур) — Как типизировать Redux-приложение на TypeScript или Flow

CodeFest
April 05, 2018

CodeFest 2018. Михаил Шатихин (СКБ Контур) — Как типизировать Redux-приложение на TypeScript или Flow

Посмотрите выступление Михаила: https://2018.codefest.ru/lecture/1313/

Расскажу о типизации приложений на TypeScript и Flow, сравню плюсы и минусы, упомяну об ограничениях Flow. Покажу, как добавить с нуля типы в Redux-приложение и получить максимум контроля над приложением. Расскажу, как типизация (интерфейсы) помогает автоматически документировать компоненты. Покажу, почему в типизированном приложении можно писать меньше тестов и из него полностью исчезают ошибки определённых классов — если компилируется, то просто работает. Раскрою негативную сторону: расскажу, насколько увеличивается время разработки и размер кодовой базы. Объясню, как типы помогают привлечь бэкендеров к разработке фронтенда.

Уровень
Фронтенд-разработчики, создающие приложения на React и Redux. Начальный и средний уровень.

CodeFest

April 05, 2018
Tweet

More Decks by CodeFest

Other Decks in Programming

Transcript

  1. Как затипизировать 
 React-Redux приложение 
 на TS или Flow

    Михаил Шатихин Ведущий инженер-программист СКБ Контур
  2. Контур.Гособлако • Модульная учётная система • Цель — захватить мир

    рынок госов • Старт проекта — январь 2017 • Работа ведётся в сжатые ср@ки !3
  3. Контур.Гособлако • Модульная учётная система • Цель — захватить мир

    рынок госов • Старт проекта — январь 2017 • 8 бэков и 1 фронтендер
  4. Хотели получить от типизации • Упростить внедрение новых бизнес фич

    • Ускорить отладку приложения • Сделать код надежнее • Избежать «детских» проблем JS !6
  5. Нет ли проблем после обновления пакета? !12 import Input from

    'ui/Input'; <Input size="large" width={600} value={props.query} onUpdate={props.onUpdate} ... /> // поменялось имя свойства onUpdate → onChange
  6. Сложно рефакторить код • Страшно вносить правки в большие проекты

    • Бесконечный Ctrl + R • Рассинхрон контрактов с бэкендом* !15
  7. Использовать строгую типизацию ✓ Опечатки ✓ Неизвестные параметры событий ✓

    Непредсказуемые входные параметры ✓ Проблемы после обновления npm пакетов ✓ Нет IntelliSense ✓ Сложно рефакторить код
  8. • Компилятор • Нужно писать типы • Умеет в ES3,

    ES5, ES6 • Инструменты ООП и ФП • Экспериментальные фичи • Анализатор • Можно не писать типы • Создан для легаси кода Typescript Flow wsd.events/2018/02/03/pres/type-dive.pdf !19
  9. Typescript JS Typescript • Язык со статической типизацией • Динамично

    развивается c 2012 • Универсален в использовании • Понятнее для бэкендеров* !21
  10. Библиотека для построения интерфейса Kontur-UI библиотека компонентов Redux Реализация FLUX

    архитектуры Контейнер состояния Прост и предсказуем в работе React !23
  11. Что в props? !27 const SearchPage = props => (

    <SearchBar // что такое props.onSearch onSearch={props.onSearch} /> ... )
  12. Текстовые константы action.type !30 const reducer = ( previousState, action

    ) => { // какие значения // может принимать action.type switch (action.type) { ... } }
  13. Что будем делать? • Будем жить с этим • Напишием

    пачку тестов • Затипизируем !31
  14. Подготовим файлы • *.js → *.ts • *.jsx → *.tsx

    • добавим tsconfig.json • ts-loader !34 reactjs.org/docs/static-type-checking
  15. Типы данных !39 const isDone: boolean; const query: string; const

    onSearch: (query: string) => void; const list: any[] = [1, true, 'free'];
  16. Строковые типы !40 const size = 'small'; // какого типа

    size const size: string = 'small'; const size: 'small' = 'small'; const size: 'mini' = 'small'; //[ts] Type '"small"' is not assignable to type '"mini"'.
  17. Псевдонимы типов !41 type Size = string; type SizeResolver =

    () => string; type SizeOrResolver = Size | SizeResolver; function getSize(s: SizeOrResolver): Size { return typeof s === "string" ? s : s(); }
  18. Объединение типов !42 type Size = 'small' | 'medium' |

    'big'; // const small: "small" | "medium" | "big" const small: Size = 'small';
  19. Контракты !43 interface Action { type: string; payload: number; }

    const anyAction: Action = { type: 'ANY_ACTION', payload: 0 }
  20. Дженерики !44 interface Action<T> { type: string; payload: T; }

    const numberAction: Action<number> = { type: 'NUMBER_ACTION', payload: 0 }
  21. Redux4 прогнулся под Typescript !45 interface Action<T = any> {

    type: T; } type Reducer< S = any, A extends Action = AnyAction > = (state: S | undefined, action: A) => S;
  22. План типизации !46 ПРИЛОЖЕН • events • props • type

    • payload • state • reducer • selectors КОМПОНЕНТЫ ДЕЙСТВИЯ СОСТОЯНИЕ
  23. Типизируем props в компонентах !48 interface Props { query: string;

    onSearch: (options: SearchParams) => void; } const SearchPage: SFC<Props> = props => ( <SearchBar query={props.query} onSearch={props.onSearch} /> )
  24. План типизации !51 ПРИЛОЖЕН • events • props • type

    • payload • state • reducer • selectors КОМПОНЕНТЫ ДЕЙСТВИЯ СОСТОЯНИЕ
  25. ActionCreator версия без типизации !54 const searchRequest = (options) =>

    ({ type: 'SEARCH_PAGE_REQUEST', payload: { ...options } })
  26. Первое приближение с типами !55 interface SearchPayload { query: string;

    } type Search = Action<SearchPayload, 'SEARCH_REQUEST'>; const searchRequest = (options: SearchPayload): Search => ({ type: 'SEARCH_REQUEST', payload: { ...options } })
  27. DRY

  28. Функция помощник actionCreator !58 function actionCreator< T extends Action<any, string>

    >(Ctor: { new(payload: T['payload']): T }) { return (payload: T['payload']): T => new Ctor(payload); }
  29. Функция помощник actionType !59 function actionType<Payload, Type>(type: Type) { return

    class implements Action<Payload, Type> { static readonly Type = type; public readonly type = type; constructor(public payload: Payload) { return { payload, type }; } }; }
  30. ActionCreator после рефакторинга !60 class Search extends actionType< { query:

    string }, 'SEARCH_REQUEST' >('SEARCH_REQUEST') { } const search = actionCreator(Search);
  31. План типизации !62 ПРИЛОЖЕН • events • props • type

    • payload • state • reducer • selectors КОМПОНЕНТЫ ДЕЙСТВИЯ СОСТОЯНИЕ
  32. Типизируем state !64 interface State { readonly query: string; readonly

    searchResult: SearchResult; } const searchPageReducer = ( previousState: State, action: AppActions ): State => { ... }
  33. Получили вывод типа action.payload !68 const searchPageReducer = ( previousState:

    State, action: AppActions ): State => { switch (action.type) { const payload = action.payload; // (property) payload: SearchResult ... } }
  34. Типизируем селектор состояния !71 const getState = ( state: State,

    ownProps: OwnProps ): State => ({ ...state, ...ownProps });
  35. Типизируем rootReducer !72 const rootReducer = combineReducers< AppState, AppActions >({

    searchPageReducer, authReducer }); interface AppState { searchPageReducer: SearchPageState; authReducer: AuthState; } type AppActions = SearchPageActions | AuthActions;
  36. Что получили • State всего приложения типизирован • Каждый reducer

    типизирован (state, action) • Автокомплит action.type в reducer • Вывод типа action.payload в reducer • Типизированные селекторы состояния !73
  37. План типизации !74 ПРИЛОЖЕН • events • props • type

    • payload • state • reducer • selectors КОМПОНЕНТЫ ДЕЙСТВИЯ СОСТОЯНИЕ
  38. Бонусы • не нужно тестировать component props • можно затипизировать

    css свойства • можно переиспользовать контракты с бэкенда !75
  39. Синхронизация моделей DTO • Автогенерация контрактов с бэкенда (*.cs →

    *.d.ts) • Model.cs → interface Model { ... } Web API Client App !78 Models.d.ts JSON API
  40. Model.cs → interface Model { ... } !79 public class

    AddressFullObjectM { public AddressM address { get; set; } public string postalCode { get; set; } public HouseM house { get; set; } public string building { get; set; } public string room { get; set; } } interface AddressFullObjectM { address: server.AddressM; building: string; house: server.HouseM; postalCode: string; room: string; }
  41. Когда применять • Система часто изменяется • Требуется высокое качество

    и скорость разработки • В крупных и open source проектах Anders Hejlsberg@Build2016: Big JavaScript codebases tend to become "read-only". Создатель: Turbo Pascal, Delphi, C#, Typescript !82