Перейти к содержимому

Ts src что это

  • автор:

Заголовочные файлы

Для установки связи с внешними файлами скриптов javascript в TS служат декларативные или заголовочные файлы. Это файлы с расширением .d.ts , они описывают синтаксис и структуру функций и свойств, которые могут использоваться в программе, не предоставляя при этом конкретной реализации. Их действие во многом похоже на работу файлов с расширением .h в языках C/C++. Они выполняют своего рода роль оберток над библиотеками JavaScript.

Рассмотрим, как мы можем использовать заголовочные файлы. Иногда в программах на javascript используются глобальные переменные, которые должны быть видны для всех функций приложения. Например, пусть на веб-странице (или во внешнем подключаемом файле javascript) в коде js определена переменная:

    Metanit.com  

Приложение на TypeScript

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

И, допустим, мы хотим использовать эту переменную message в коде TypeScript в файле app.ts:

console.log(message);

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

declare let message: string;

С помощью ключевого слова declare в программу на TS подключается определение глобальной переменной.

То есть у нас получится следующая структура проекта:

  • app.ts
  • globals.d.ts
  • index.html

Компиляция

Если мы компилируем, передавая компилятору в консоли название файла:

tsc app.ts

То в этом случае компилятор не найдет автоматически файл globals.d.ts. В этом случае нам надо в файле app.ts явно указать расположение файла globals.d.ts с помощью директивы reference

/// console.log(message);

Заголовочные файлы в TypeScript

Если же мы полагаемся на файл конфигурации tsconfig.json, просто выполняя команду

Подобным образом мы можем подключать другие компоненты кода JavaScript — функции, объекты, классы. Рассмотрим их подключение.

Функции

Пусть на веб-странице в коде js объявлены две следующие функции:

let message = «Hello TypeScript!»; function hello() < console.log(message); >function sum(a, b)

Функция hello() выводит значение переменной message на консоль, а функция sum() возвращает сумму двух чисел.

И, допустим, в коде TS мы хотим вызывать эти функции:

hello(); let result = sum(2, 5); console.log(result);

В этом случае подключение в файле globals.d.ts выглядело бы так:

declare function hello(): void; declare function sum(a: number, b: number): number;

Подключение объектов

Пусть в коде JavaScript есть следующий объект:

const tom = < name: "Tom", age: 37, print()< console.log(`Name: $Age: $`); > >

Используем этот объект в коде typescript:

tom.print();

В этом случае определение объекта в файле globals.d.ts выглядело бы так:

declare const tom: void>;

Подключени сложных объектов

Однако может возникнуть сложность с подключением более сложных объектов. Например, пусть есть такой объект javascript:

var points = [< X: 10, Y: 34 >, < X: 24, Y: 65 >, < X: 89, Y: 12 >];

Для данного массива объектов в файле globals.d.ts мы можем определить соответствующий отдельному объекту интерфейс и подключить массив объектов некоторого интерфейса, который содержит два свойства X и Y:

interface IPoint < X: number; Y: number; >declare var points: IPoint[];

И в TS мы сможем использовать этот массив:

for (let point of points) < console.log(`Точка с координатами X = $Y = $`); >

Консольный вывод браузера:

Точка с координатами X = 10 Y = 34 Точка с координатами X = 24 Y = 65 Точка с координатами X = 89 Y = 12

Подключение классов

Рассмотрим последний пример — подключение в typescript классов, определенных в javascript. Пусть в коде JavaScript определен следующий класс Person:

class Person < constructor(name, age)< this.name = name; this.age = age; >display() < console.log(this.name, this.age); >>

Для этого класса в файле globals.d.ts определим следующее объявление класса:

declare class Person

Для класса прописываем все его поля и методы, при этом методы (в том числе конструктор) не имеют реализации, для них только определяются параметры и их типы и тип возвращаемого значения.

И в коде TypeScript используем этот класс:

let tom = new Person("Tom", 37); tom.display(); // Tom 37 console.log(tom.name); // Tom

Статическая типизация

These docs are old and won’t be updated. Go to react.dev for the new React docs.

Check out React TypeScript cheatsheet for how to use React with TypeScript.

Инструменты для статической типизации, такие как Flow или TypeScript, позволяют отлавливать большую часть ошибок ещё до исполнения кода. Кроме того, они существенно улучшают процессы разработки, добавляя автодополнение и другие возможности. Для приложений с большой кодовой базой мы рекомендуем использовать Flow или TypeScript вместо PropTypes .

Flow — это библиотека для статической типизации JavaScript, разработанная в Facebook и часто применяемая в связке с React. Flow расширяет возможности JavaScript, добавляя аннотации типов для переменных, функций и React-компонентов. Ознакомиться с основами Flow можно на странице официальной документации.

Чтобы начать пользоваться возможностями Flow необходимо:

  • добавить Flow в ваш проект как зависимость.
  • убедиться, что аннотации Flow удаляются из кода при его компиляции.
  • добавить несколько аннотаций типов и запустить Flow для их проверки;

Рассмотрим подробнее каждый из этих шагов.

Добавление Flow в проект

Убедитесь, что вы находитесь в директории проекта, после чего запустите одну из следующих команд:

Если вы используете Yarn:

yarn add --dev flow-bin

Если вы используете npm:

npm install --save-dev flow-bin

Эти команды добавят последнюю версию Flow в ваш проект.

Далее нужно добавить flow в секцию «scripts» файла package.json :

 // . "scripts":   "flow": "flow", // . >, // . >

Теперь можно запустить скрипт, прописав в терминале:

yarn run flow init
npm run flow init

Эти команды создадут файл с исходной конфигурацией Flow, который обязательно нужно закоммитить.

Удаление аннотаций Flow из скомпилированного кода

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

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

Create React App

Если для изначальной конфигурации проекта вы выбрали Create React App, вам ничего не нужно делать! Проект уже настроен должным образом и аннотации Flow должны удаляться при сборке проекта.

Примечание:

Дальнейшие инструкции рассчитаны на тех, кто не использует Create React App, т. к. там уже есть все необходимые настройки для работы с Flow.

Если для своего проекта вы самостоятельно настраивали Babel, нужно установить специальный пресет для работы с Flow:

yarn add --dev @babel/preset-flow
npm install --save-dev @babel/preset-flow

Затем добавьте установленный пресет flow в свою конфигурацию Babel. Например так, если вы используете конфигурационный файл .babelrc :

 "presets": [ "@babel/preset-flow", "react" ] >

Этот пресет позволит использовать Flow в вашем коде.

Примечание:

Для работы с Flow не требуется отдельно устанавливать пресет react — Flow уже понимает JSX-синтаксис. Тем не менее, часто используют оба пресета одновременно.

Другие инструменты сборки

Для удаления аннотаций Flow существует отдельная библиотека: flow-remove-types. Она может пригодиться, если вы пользуетесь другими инструментами для сборки проекта.

Если всё было сделано правильно, можно попробовать запустить процесс Flow:

yarn flow
npm run flow

Вы должны увидеть примерно такое сообщение в терминале:

No errors! ✨ Done in 0.17s.

Добавление аннотаций типов

По умолчанию Flow проверяет только файлы, содержащие специальную аннотацию (обычно её указывают в самом начале файла):

// @flow

Попробуйте добавить эту аннотацию в некоторые файлы вашего проекта, а затем запустить скрипт yarn flow или npm run flow и посмотреть, найдёт ли Flow какие-нибудь ошибки.

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

Всё должно работать! Советуем изучить Flow подробнее, ознакомившись со следующими ресурсами:

  • Flow Documentation: Type Annotations
  • Flow Documentation: Editors
  • Flow Documentation: React
  • Linting in Flow

TypeScript — это язык программирования, разработанный в Microsoft. TypeScript является надмножеством JavaScript, имеет статическую систему типов и собственный компилятор. Статическая типизация позволяет отлавливать ошибки и баги во время компиляции, ещё до запуска приложения. Подробнее узнать о совместном использовании TypeScript и React можно здесь.

Чтобы использовать TypeScript, нужно:

  • добавить TypeScript в проект как зависимость.
  • настроить компилятор.
  • использовать правильные расширения файлов.
  • установить файлы объявлений для используемых библиотек;

Остановимся подробнее на каждом из этих моментов.

Использование TypeScript вместе с Create React App

Create React App поддерживает TypeScript по умолчанию.

Чтобы создать новый проект с поддержкой TypeScript, используйте следующую команду:

npx create-react-app my-app --template typescript

Можно добавить поддержку TypeScript в уже существующий проект, как показано здесь.

Примечание:

Дальше описывается ручная настройка TypeScript. Если вы используете Create React App, можете пропустить этот раздел.

Добавление TypeScript в проект

Всё начинается с одной единственной команды в терминале:

yarn add --dev typescript
npm install --save-dev typescript

Ура! Вы установили последнюю версию TypeScript. Теперь в вашем распоряжении новая команда — tsc . Но прежде, чем праздновать, давайте добавим соответствующий скрипт в файл package.json :

 // . "scripts":   "build": "tsc", // . >, // . >

Настройка компилятора TypeScript

Сам по себе компилятор бесполезен, пока мы не скажем, что именно ему нужно делать. Для этого есть специальный конфигурационный файл tsconfig.json . Создадим этот файл:

yarn run tsc --init
npx tsc --init

Сгенерированный файл tsconfig.json уже содержит несколько параметров, которые используются компилятором по умолчанию. Кроме того, можно указать множество опциональных параметров. Более детальная информация по каждому параметру находится здесь.

Из всех параметров больше всего сейчас нас интересуют rootDir и outDir . Очевидно, что компилятор берёт исходный TypeScript код, и компилирует его в JavaScript. И нам нужно, чтобы не возникло путаницы между исходными файлами и сгенерированным кодом.

Эту проблему можно решить в два шага:

  • Во-первых, изменим структуру проекта. Все файлы с исходниками переместим в директорию src .
├── package.json ├── src │ └── index.ts └── tsconfig.json
  • Затем, укажем компилятору откуда ему брать исходные файлы и куда сохранять скомпилированный код.
// tsconfig.json  "compilerOptions":  // . "rootDir": "src", "outDir": "build" // . >, >

Отлично! Теперь, если мы запустим скрипт сборки проекта, компилятор сохранит готовый JavaScript в директорию build . В TypeScript React Starter уже есть готовый tsconfig.json с неплохим набором параметров для дальнейшей тонкой настройки под себя.

Как правило, скомпилированный JavaScript-бандл не следует хранить в системе контроля версий, так что не забудьте добавить папку build в файл .gitignore .

В React мы почти всегда используем .js в качестве расширений файлов компонентов. В TypeScript лучше разделять файлы на два типа:

.tsx для файлов, содержащих разметку JSX , и .ts для всего остального.

Если всё было сделано правильно, можно попробовать скомпилировать TypeScript:

yarn build
npm run build

Если эта команда не вывела ничего в терминале, то процесс компиляции прошёл успешно.

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

Существует два основных способа получения файлов объявлений:

Bundled — библиотека устанавливается вместе с собственным файлом объявлений. Это прекрасный вариант для нас, так как всё, что нам нужно — установить нужный пакет. Чтобы проверить, есть ли у библиотеки файл объявлений, поищите index.d.ts в её исходных файлах. В некоторых библиотеках наличие и расположение этого файла указываются в package.json в секциях typings или types .

DefinitelyTyped — это внушительный репозиторий файлов объявлений. Например, React устанавливается без собственного файла объявления — вместо этого мы устанавливаем его отдельно:

# yarn yarn add --dev @types/react # npm npm i --save-dev @types/react

Иногда пакет, который вы хотите использовать, не имеет ни собственного файла объявлений, ни соответствующего файла в репозитории DefinitelyTyped. В этом случае, мы можем объявить собственный локальный файл объявлений. Для этого надо создать файл declarations.d.ts в корне директории, где лежат исходники вашего проекта. Файл объявлений может выглядеть примерно так:

declare module 'querystring'  export function stringify(val: object): string export function parse(val: string): object >

Вот и всё, вы готовы писать код на TypeScript! Чтобы познакомиться с ним поближе, рекомендуем посетить эти ресурсы:

  • TypeScript Documentation: Everyday Types
  • TypeScript Documentation: Migrating from JavaScript
  • TypeScript Documentation: React and Webpack

Reason — это не новый язык, а новый синтаксис и набор инструментов для проверенного временем языка OCaml. Reason предоставляет синтаксис, ориентированный на JavaScript-программистов, и использует уже известный всем способ распространения через NPM/Yarn.

Reason был разработан в Facebook и используется в некоторых продуктах этой компании — например, в Messenger. Reason всё ещё считается довольно экспериментальным инструментом, но уже имеет библиотеку привязок для React, поддерживаемую Facebook, а также отзывчивое сообщество.

Kotlin — это язык со статической типизацией, разработанный в JetBrains. Он нацелен на платформы работающие на основе JVM, Android, LLVM и JavaScript.

JetBrains разрабатывает и поддерживает несколько библиотек специально для сообщества React: React bindings совместно с Create React Kotlin App. Последняя позволит вам начать использовать Kotlin вместе с React в одном проекте без необходимости ручной конфигурации.

Помните: есть и другие языки со статической типизацией, которые могут компилироваться в JavaScript, а значит — совместимы с React. Например, F#/Fable вместе с elmish-react. Для подробной информации переходите на соответствующие сайты и не стесняйтесь предлагать больше React-совместимых статически типизированных языков в этот раздел!

# SFC и синтаксис

— синтаксический сахар, обрабатываемый на этапе компиляции, для использования Composition API в однофайловых компонентах (SFC). Это рекомендуемый синтаксис при использовании однофайловых компонентов и Composition API. Он предлагает ряд преимуществ по сравнению с обычным синтаксисом :

  • Более лаконичный код с меньшим количеством boilerplate-кода
  • Возможность объявлять входные параметры и генерируемые события с использованием чистого TypeScript
  • Лучшая производительность во время выполнения (шаблон компилируется в render-функцию в той же области видимости, без промежуточной прокси)
  • Лучшая производительность IDE при определении типов (меньше работы для языкового сервера по извлечению типов из кода)

# Базовый синтаксис

Чтобы использовать синтаксис, добавьте атрибут setup в секцию :

script setup> console.log('привет синтаксис script setup') script> 

Код внутри компилируется как содержимое функции компонента setup() . Это означает, что в отличие от обычного , который выполняется только один раз при первом импорте компонента, код внутри будет выполняться каждый раз при создании экземпляра компонента.

# Привязки верхнего уровня будут доступны в шаблоне

При использовании любые привязки верхнего уровня (в т.ч. переменные, объявления функций и импорты) объявленные внутри будут доступны напрямую в шаблоне:

script setup> // переменная const msg = 'Hello!' // функция function log()  console.log(msg) > script> template> div @click="log">>div> template> 

1
2
3
4
5
6
7
8
9
10
11
12
13

Импорты объявляются таким же образом. Это означает, что можно напрямую использовать импортированную вспомогательную функцию в выражениях шаблона, без необходимости объявлять её через опцию methods :

script setup> import  capitalize > from './helpers' script> template> div>>div> template> 

1
2
3
4
5
6
7

# Реактивность

Реактивное состояние нужно явно создавать с помощью API реактивности. Аналогично значениям, возвращаемым из функции setup() , ref-ссылки автоматически разворачиваются, когда на них ссылаются в шаблонах:

script setup> import  ref > from 'vue' const count = ref(0) script> template> button @click="count++">>button> template> 

1
2
3
4
5
6
7
8
9

# Использование компонентов

Значения в области видимости также могут быть использованы непосредственно в качестве имён тегов пользовательских компонентов:

script setup> import MyComponent from './MyComponent.vue' script> template> MyComponent /> template> 

1
2
3
4
5
6
7

Считайте, что на MyComponent ссылаются как на переменную. Если использовали JSX, то ментальная модель тут аналогична. Эквивалент в kebab-case работает и в шаблоне — но настоятельно рекомендуем писать теги в PascalCase для консистентности. Это также помогает отличить их от нативных пользовательских элементов.

# Динамические компоненты

Поскольку на компоненты ссылаются как на переменные, а не регистрируют их под строковыми ключами, то внутри при использовании динамических компонентов потребуется использовать динамическую привязку с помощью :is :

script setup> import Foo from './Foo.vue' import Bar from './Bar.vue' script> template> component :is="Foo" /> component :is="someCondition ? Foo : Bar" /> template> 

1
2
3
4
5
6
7
8
9

Обратите внимание, как компоненты могут использоваться в качестве переменных в тернарном выражении.

# Рекурсивные компоненты

Однофайловые компоненты могут неявно ссылаться сами на себя с помощью имени файла. Например, файл с именем FooBar.vue может ссылаться на себя как в своём шаблоне.

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

import  FooBar as FooBarChild > from './components' 

# Компоненты с пространством имён

Можно использовать теги компонентов с точками, например , чтобы ссылаться на компоненты, вложенные в свойства объекта. Это полезно при импорте нескольких компонентов из одного файла:

script setup> import * as Form from './form-components' script> template> Form.Input> Form.Label>labelForm.Label> Form.Input> template> 

1
2
3
4
5
6
7
8
9

# Использование пользовательских директив

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

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

script setup> const vMyDirective =  beforeMount: (el) =>  // сделать что-нибудь с элементом > > script> template> h1 v-my-directive>Какой-то заголовокh1> template> 

1
2
3
4
5
6
7
8
9
10
11

script setup> // импорт также работает, его можно переименовать для соответствия схеме именования import  myDirective as vMyDirective > from './MyDirective.js' script> 

# defineProps и defineEmits

Чтобы объявить props и emits при использовании нужно использовать API defineProps и defineEmits , которые предоставляют полную поддержку вывода типов и автоматически доступны внутри :

script setup> const props = defineProps( foo: String >) const emit = defineEmits(['change', 'delete']) // код setup script> 

1
2
3
4
5
6
7
8
9

  • defineProps и defineEmits — макросы компилятора используемые только внутри . Их не нужно импортировать и они будут компилироваться при обработке .
  • defineProps принимает то же значение, что и опция props , а defineEmits принимает то же значение, что и опция emits .
  • defineProps и defineEmits предоставляют правильный вывод типов на основе переданных опций.
  • Опции, переданные в defineProps и defineEmits будут подняты из setup в область видимости модуля. Поэтому опции не могут ссылаться на локальные переменные, объявленные в области видимости setup. Это приведёт к ошибке компиляции. Однако, они могут ссылаться на импортированные привязки, поскольку они также находятся в области видимости модуля.

# defineExpose

Компоненты со по умолчанию закрытые — т.е. публичный экземпляр компонента, получаемый через ссылку в шаблоне или цепочку $parent , не объявляет доступа к каким-либо привязкам внутри .

Чтобы явно объявить свойства в компоненте со , воспользуйтесь макросом компилятора defineExpose :

script setup> import  ref > from 'vue' const a = 1 const b = ref(2) defineExpose( a, b >) script> 

1
2
3
4
5
6
7
8
9
10
11

Когда родитель запросит экземпляр этого компонента через ссылку в шаблоне, то полученный экземпляр будет иметь вид < a: number, b: number >(ref-ссылки автоматически разворачиваются, как и для обычных экземпляров).

# useSlots и useAttrs

Использование slots и attrs внутри должно встречаться крайне редко, так как в шаблоне прямой доступ к ним можно получить через $slots и $attrs . В редких случаях, когда они всё же нужны, можно воспользоваться вспомогательными методами useSlots и useAttrs соответственно:

script setup> import  useSlots, useAttrs > from 'vue' const slots = useSlots() const attrs = useAttrs() script> 

1
2
3
4
5
6

useSlots и useAttrs — фактически будут runtime-функциями, которые возвращают эквивалент setupContext.slots и setupContext.attrs . Они также могут быть использованы в обычных функциях composition API.

# Использование вместе с обычной секцией

можно использовать и вместе с обычной секцией . Обычный может понадобиться в случаях, когда необходимо:

  • Объявление опций, которые не могут быть выражены в , например inheritAttrs или пользовательские опции, добавляемые плагинами.
  • Объявление именованных экспортов.
  • Запуск побочных эффектов или создание объектов, которые должны выполняться только один раз.
script> // обычный , выполняется в области видимости модуля (только один раз) runSideEffectOnce() // объявление дополнительных опций export default  inheritAttrs: false, customOptions: > > script> script setup> // выполняется в области видимости setup() (для каждого экземпляра) script> 

1
2
3
4
5
6
7
8
9
10
11
12
13
14

render -функции не поддерживаются в таком сценарии. Вместо них используйте обычный с опцией setup .

# Верхне-уровневый await

Верхне-уровневый await можно использовать внутри . Полученный код будет скомпилирован как async setup() :

script setup> const post = await fetch(`/api/post/1`).then(r => r.json()) script> 

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

async setup() нужно использовать в сочетании с Suspense , которая в настоящее время всё ещё является экспериментальной функцией. Её планируется доработать и документировать в одном из будущих релизов — но, если интересно, можно посмотреть её тесты

(opens new window) , чтобы увидеть как она работает.

# Возможности только для TypeScript

# Объявление типов для входных параметров/генерируемых событий

Типы входных параметров и генерируемых событий также можно объявить с помощью синтаксиса с передачей аргумента литерального типа в defineProps или defineEmits :

const props = defineProps foo: string bar?: number >>() const emit = defineEmits (e: 'change', id: number): void (e: 'update', value: string): void >>() 

1
2
3
4
5
6
7
8
9

  • defineProps или defineEmits могут использовать только объявления во время runtime ИЛИ объявление типа. Использование обоих одновременно приведёт к ошибке компиляции.
  • При использовании объявления типа, эквивалент объявления в runtime автоматически генерируется на основе статического анализа для устранения необходимости двойного объявления и обеспечении корректного поведения во время выполнения.
    • В режиме разработки компилятор попытается вывести из типов соответствующую валидацию в runtime. Например, foo: String выводится из типа foo: string . Если тип будет ссылкой на импортированный тип, то результатом выведения будет foo: null (аналогичный типу any ), так как компилятор не имеет информации о внешних файлах.
    • В режиме production компилятор сгенерирует массив форматов объявлений для уменьшения размера сборки (входные параметры здесь будут скомпилированы в [‘foo’, ‘bar’] )
    • Выдаваемый код по-прежнему останется TypeScript с правильной типизацией, который может обрабатываться другими инструментами.
    • Литерал типа
    • Ссылка на интерфейс или литерал типа в том же файле

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

    # Значения входных параметров по умолчанию при использовании объявления типов

    Один из недостатков объявления типов через defineProps в том, что нет возможности указать значения по умолчанию для входных параметров. Для решения этой проблемы создан макрос для компилятора withDefaults :

    interface Props  msg?: string labels?: string[] > const props = withDefaults(definePropsProps>(),  msg: 'привет', labels: () => ['один', 'два'] >) 

    1
    2
    3
    4
    5
    6
    7
    8
    9

    Это скомпилируется в эквиваленты опций default в runtime. Кроме того, withDefaults предоставляет проверку типов для значений по умолчанию и гарантирует, что возвращаемый тип props будет с удалёнными опциональными флагами для свойств, у которых указаны значения по умолчанию.

    # Ограничение: Никаких импортов с помощью src

    Из-за разницы в семантике выполнения модулей код внутри полагается на контекст однофайлового компонента. При перемещении во внешние файлы .js или .ts , это может привести к путанице как для разработчиков, так и для инструментов. Поэтому нельзя использовать с атрибутом src .

    (opens new window)
    Последнее обновление страницы: больше 1 года назад

    Когда следует переводить проект с JavaScript на TypeScript и как это сделать

    Любую кодовую базу на JavaScript со временем становится тяжело поддерживать. С каждой новой строчкой кода сложнее вносить изменения — исправления в одном месте ведут к поломкам в другом, и добавление нового функционала редко проходит без сложностей и увеличений бюджетов.

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

    На сегодняшний день, самой популярной альтернативой считается TypeScript. Это не самостоятельный язык программирования, а надмножество JS, которое расширяет возможности разработки веб-приложений, помогает лучше понимать код, ускоряет работу с задачами и предотвращает баги до их отправки на сервер.

    Итак, при создании новых проектов лучше выбирать TypeScript, но что делать если нужно доработать существующую систему на JS? Именно с такой ситуацией мы столкнулись приступив к редизайну сайта нашего клиента CarPrice.

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

    О клиенте

    CarPrice — российский сервис по продаже подержанных автомобилей через онлайн-аукцион.

    Риски проекта

    Отдел маркетинга CarPrice запланировал ряд активностей по развитию сервиса, многие из которых требовали доработок сайта, включая редизайн главной страницы.

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

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

    Решение

    Перевести текущую кодовую базу фронтэнд-приложения на более надежную технологию — TypeScript. Чтобы объяснить, как именно это решение должно помочь устранить проблему предлагаем подробнее рассмотреть преимущества переноса.

    Ключевые достоинства TypeScript

    Технологию разработала компания Microsoft и сделала ее общедоступной. Популярность TypeScript постоянно растет — за первый квартал 2020 года технология заняла 7 строчку рейтинга GitHub по количеству pull-реквестов, обогнав PHP и C. Ниже подробнее разберем, за какие достоинства TypeScript так полюбился разработчикам, и почему его выбирают Slack и AirBnb.

    • Строгая типизация; Для работы с кодом прописываются типы. Например type-safe определяет операции, которые можно выполнять с данными. Если тип данных не поддерживает операцию, то язык не позволит вам ее выполнить. Так код становится более защищенным.
    • Удобочитаемость; Благодаря типам код становится понятнее. Его легче читать, а значит и подключение новых разработчиков происходит быстрее. Компилятор и IDE сразу подскажут, какие аргументы принимает функциональный код, какие значения отдает, как манипулирует внешними данными и многое другое.
    • Стандартизация рабочего процесса; Подобно подходу TDD, при котором разработчик сначала создает тесты для проверки системы и только после этого реализует функционал, TS побуждает вас подумать об интерфейсе вашего кода, прежде чем переходить к его внутренней реализации.
    • Помощь в предотвращении багов; Если код компилируется, высок процент вероятности, что он работает. Одно исследование показало, что TS способен отлавливать до 15% всех ошибок JS.
    • Упрощенный рефакторинг; С TS не страшно вносить изменения в кодовую базу — среда разработки поможет найти все варианты использования реорганизованных частей кода, укажет на измененные классы, функции и объекты, предупредит об ошибке компиляции, в случае несоответствия типов после рефакторинга.
    • Обратная совместимость с JavaScript. Сравнивать JS и Typescript не совсем корректно, так как они по сути взаимозаменяемы и любой JS код является валидным кодом TS. Поэтому выбор технологии в основном влияет не на качество финального продукта, а на удобство разработчика, скорость и стоимость создания фронтенд-приложения.

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

    К сожалению, нельзя взять репозиторий с JavaScript кодом и мгновенно переключить его на идиоматический TypeScript. На это потребуется время. Если бы не требовалось столько доработок, то можно было бы оставить всё как есть. Но в данном случае миграцию оправдывали планы по развитию и масштабированию системы.

    Как перевести проект на TypeScript

    Итак, достоинства инструмента очевидны. Остается теперь оценить трудозатраты переноса. Существует несколько стратегий миграции кодовой базы проекта:

    1. Гибридная стратегия миграции; При таком подходе осуществляется пофайловый перевод. В ходе этого процесса программисты вручную редактируют файлы, исправляют ошибки типизации. Главный недостаток подхода связан с большими время затратами на миграцию — на полный перенос крупного проекта уйдет несколько месяцев.
    2. Всеобъемлющая стратегия миграции. При помощи программных инструментов и анализаторов кода проект автоматически переводится на TypeScript. Места, где TypeScript находит ошибки помечаются игнорируемыми с помощью комментария @ts-ignore.

    Так как у нас не было нескольких недель для переноса, мы выбрали вторую стратегию.

    Для автоматического переноса мы выбрали инструмент ts-migrate от разработчиков Airbnb. Далее расскажем, как им пользоваться.

    Перед началом миграции необходимо установить typescript и ts-migrate.npm i —save-dev typescript ts-migrate. Для начала выполните команду npx ts-migrate-full ./src Вместо .js файлов появятся файлы с расширениями .ts, .tsx и файл конфигурации typescript tsconfig.json.

    В самих файлах почти в каждой строчке появятся комментарии:

    Но благодаря этим комментариям проект сохраняет работоспособность. Чтобы ощутить все преимущества от миграции нужно удалить комментарии и исправить появившиеся ошибки.

    С более тонкой настройкой процесс перевода можно сделать менее болезненным. Для этого выполним команду npx ts-migrate init ./src.

    Она создаст файл конфигурации typescript tsconfig.json.

    Параметр strict: true внесет много ограничений, поэтому на следующем шаге замените содержимое файла на следующее:

    Затем выполните команды npx ts-migrate rename ./src и npx ts-migrate migrate ./src. Большинство комментариев уйдет и файл компонента будет выглядеть приблизительно так:

    В нашем небольшом проекте из 25 000 строк кода обнаружилось около 400 мест, где все еще можно было найти ошибки при сборке. На их исправление потребовалось пять часов. В конце осталось только настроить webpack и добавить лоадеры. Так мы получили работающее приложение на TypeScript. С более крупными системами миграция этим способом может занимать пару недель.

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

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

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

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