- PVSM.RU - https://www.pvsm.ru -
В Airbnb для фронтенд-разработки официально применяется TypeScript (TS). Но процесс внедрения TypeScript и перевода на этот язык зрелой кодовой базы, состоящей из тысяч JavaScript-файлов, это — не дело одного дня. А именно, внедрение TS происходило в несколько этапов. Сначала это было предложение, через некоторое время язык начали применять во множестве команд, потом внедрение TS вышло в бета-фазу. В итоге же TypeScript стал официальным языком фронтенд-разработки Airbnb. Подробнее о процессе внедрения TS в Airbnb рассказано здесь [1].
Этот материал посвящён описанию процессов перевода больших проектов на TypeScript и рассказу о специализированном инструменте, ts-migrate, разработанном в Airbnb.
Перевод крупномасштабного проекта с JavaScript на TypeScript — это сложная задача. Мы, приступая к её решению, исследовали две стратегии перехода с JS на TS.
При таком подходе осуществляется постепенный, пофайловый перевод проекта на TypeScript. В ходе этого процесса редактируют файлы, исправляют ошибки типизации и работают так до тех пор, пока на TS не будет переведён весь проект. Параметр allowJS [3] позволяет иметь в проекте и TypeScript-файлы и JavaScript-файлы. Благодаря этому такой подход к переводу JS-проектов на TS вполне жизнеспособен.
При использовании гибридной стратегии миграции не нужно приостанавливать процесс разработки, можно постепенно, файл за файлом, переводить проект на TypeScript. Но, если говорить о крупномасштабном проекте, этот процесс может занять много времени. Кроме того, это потребует обучения программистов, работающих в разных отделах организации. Программистов нужно будет знакомить с особенностями проекта.
При таком подходе берётся проект, написанный исключительно на JavaScript, или такой, часть которого написана на TypeScript, и полностью преобразовывается в TypeScript-проект. При этом понадобится использовать тип any и комментарии @ts-ignore, что позволит проекту компилироваться без ошибок. Но со временем код можно отредактировать и перейти к использованию более подходящих типов.
У всеобъемлющей стратегии миграции на TypeScript есть несколько серьёзных преимуществ перед гибридной стратегией:
Если учесть вышесказанное, то может показаться, что всеобъемлющая миграция по всем показателям превосходит гибридную миграцию. Но всеобъемлющий перевод зрелой кодовой базы на TypeScript — это очень трудная задача. Для её решения мы решили прибегнуть к скриптам для модификации кода, к так называемым «кодмодам» (codemods [4]). Когда мы только начали перевод проекта на TypeScript, делая это вручную, мы обратили внимание на повторяющиеся операции, которые можно было бы автоматизировать. Мы написали кодмоды для каждой из таких операций и объединили их в единый конвейер миграции.
Опыт подсказывает нам, что нельзя быть на 100% уверенным в том, что после автоматического перевода проекта на TypeScript в нём не будет ошибок. Но мы выяснили, что комбинация шагов, описанная ниже, позволила нам добиться наилучших результатов и, в итоге, получить TypeScript-проект, лишённый ошибок. Мы, используя кодмоды, смогли перевести на TypeScript проект, содержащий более 50000 строк кода и представленный более чем 1000 файлами. На это у нас ушёл один день.
На основе конвейера, показанного на следующем рисунке, мы создали инструмент ts-migrate.

Кодмоды ts-migrate
В Airbnb значительная часть фронтенда написана с использованием React [5]. Именно поэтому некоторые части кодмодов имеют отношение к концепциям, специфичным для React. Средство ts-migrate может быть использовано и с другими библиотеками или фреймворками, но это потребует его дополнительной настройки и тестирования.
Пройдёмся по основным шагам, которые нужно выполнить для перевода проекта с JavaScript на TypeScript. Поговорим и о том, как именно реализованы эти шаги.
Первое, что создают в каждом TypeScript-проекте, это — файл tsconfig.json. Ts-migrate может, если нужно, сделать это самостоятельно. Существует стандартный шаблон этого файла. Кроме того, имеется система проверок, которая позволяет обеспечить единообразную конфигурацию всех проектов. Вот пример базовой конфигурации:
{
"extends": "../typescript/tsconfig.base.json",
"include": [".", "../typescript/types"]
}
После того, как файл tsconfig.json находится там, где он должен быть, выполняется переименование файлов с исходным кодом. А именно, расширения .js/.jsx меняются на .ts/.tsx. Этот шаг очень легко автоматизировать. Это позволяет избавиться от большого объёма ручного труда.
А теперь пришло время запускать кодмоды! Мы называем их «плагинами». Плагины для ts-migrate — это кодмоды, у которых есть доступ к дополнительной информации через языковой сервер TypeScript. Плагины принимают на вход строки и выдают изменённые строки. Для выполнения трансформаций кода может быть использован набор инструментов jscodeshift [6], API TypeScript, средства обработки строк или другие инструменты для модификации AST.
После выполнения каждого из вышеописанных шагов мы проверяем, имеются ли в истории Git какие-то изменения, ожидающие включения в проект, и включаем их в проект. Это позволяет разделить миграционные PR на коммиты, что облегчает понимание происходящего и помогает отслеживать изменения в именах файлов.
Мы разделили ts-migrate на 3 пакета:
Поступив так, мы смогли отделить логику трансформации кода от ядра системы и смогли создать множество конфигураций, рассчитанных на решение разных задач. Сейчас у нас есть две основных конфигурации: migration [10] и reignore [11].
Цель применения конфигурации migration заключается в переводе проекта с JavaScript на TypeScript. А конфигурация reignore применяется для того чтобы сделать возможной компиляцию проекта, просто игнорируя все ошибки. Эта конфигурация полезна в случаях, когда имеется большая кодовая база и с ней выполняют различные действия наподобие следующих:
При таком подходе мы можем перевести проект на TypeScript даже в том случае, если при его компиляции выдаются ошибки, с которыми мы не планируем разбираться немедленно. Это упрощает и обновление TypeScript или используемых в коде библиотек.
Обе конфигурации работают на сервере ts-migrate-server, который состоит из двух частей:
interface MigrateParams {
rootDir: string; // Путь к корневой директории.
config: MigrateConfig; // Настройки миграции, включая список
// плагинов.
server: TSServer; // Экземпляр форка TSServer.
}
Это средство выполняет следующие действия:
tsconfig.json.semanticDiagnostics, syntacticDiagnostics и suggestionDiagnostics. Мы используем эти проверки для нахождения в исходном коде проблемных мест. Основываясь на уникальном коде диагностики и на номере строки в файле, мы можем идентифицировать возможный тип проблемы и применить необходимые модификации кода.
Примеры использования ts-migrate-server можно найти в пакете examples [21] или в пакете main [22]. В ts-migrate-example, кроме того, содержатся базовые примеры плагинов [23]. Они делятся на 3 основные категории:
В репозитории имеется набор примеров, направленных на демонстрацию процесса создания простых плагинов всех этих видов. Там же показано и их использование в комбинации c ts-migrate-server. Вот пример конвейера миграции [27], преобразующего код. На его вход поступает такой код:
function mult(first, second) {
return first * second;
}
А выдаёт он следующее:
function tlum(tsrif: number, dnoces: number): number {
console.log(`args: ${arguments}`);
return tsrif * dnoces;
}
В этом примере ts-migrate выполнил 3 трансформации:
first -> tsrif.function tlum(tsrif, dnoces) -> function tlum(tsrif: number, dnoces: number): number.console.log(‘args:${arguments}’);
Настоящие плагины расположены в отдельном пакете — ts-migrate-plugins [9]. Взглянем на некоторые из них. У нас имеются два плагина, основанных на jscodeshift: explicitAnyPlugin и declareMissingClassPropertiesPlugin. Набор инструментов jscodeshift [6] позволяет преобразовывать AST в обычный код, используя пакет recast [28]. Мы можем, воспользовавшись функцией toSource(), напрямую обновлять исходный код, содержащийся в наших файлах.
Плагин explicitAnyPlugin [29] берёт с языкового сервера TypeScript информацию обо всех ошибках semanticDiagnostics и о строках, в которых выявлены эти ошибки. Затем в эти строки добавляется аннотация типа any. Этот подход позволяет исправлять ошибки, так как использование типа any позволяет избавиться от ошибок компиляции.
Вот пример кода до обработки:
const fn2 = function(p3, p4) {}
const var1 = [];
Вот — тот же код, обработанный плагином:
const fn2 = function(p3: any, p4: any) {}
const var1: any = [];
Плагин declareMissingClassPropertiesPlugin [30] берёт все диагностические сообщения с кодом ошибки 2339 (можете догадаться о том, что значит [31] этот код?) и, если может найти объявления классов с пропущенными идентификаторами, добавляет их в тело класса с аннотацией типа any. Из названия плагина можно сделать вывод о том, что он применим только к ES6-классам [32].
Следующая категория плагинов основана на AST TypeScript. Обрабатывая AST, мы можем сгенерировать массив обновлений, которые нужно внести в файл с исходным кодом. Описания этих обновлений выглядят так:
type Insert = { kind: 'insert'; index: number; text: string };
type Replace = { kind: 'replace'; index: number; length: number; text: string };
type Delete = { kind: 'delete'; index: number; length: number };
После генерирования сведений о необходимых обновлениях остаётся лишь внести их в файл в обратном порядке. Если, выполнив эту операцию, мы получим новый программный код, мы соответствующим образом обновим файл с исходным кодом.
Взглянем на следующую пару плагинов, основанных на AST. Это — stripTSIgnorePlugin и hoistClassStaticsPlugin.
Плагин stripTSIgnorePlugin [33] — это первый плагин, используемый в конвейере миграции. Он убирает из файла все комментарии @ts-ignore (эти комментарии позволяют нам сообщать компилятору о том, что он должен игнорировать ошибки, происходящие в следующей строке). Если мы занимаемся переводом на TypeScript проекта, написанного на JavaScript, то этот плагин не будет выполнять никаких действий. Но если речь идёт о проекте, который частично написан на JS, а частично — на TS (несколько наших проектов пребывали в подобном состоянии), то это — первый шаг миграции, без которого нельзя обойтись. Только после удаления комментариев @ts-ignore компилятор TypeScript сможет выдавать диагностические сообщения об ошибках, которые нужно исправлять.
Вот код, поступающий на вход этого плагина:
const str3 = foo
? // @ts-ignore
// @ts-ignore comment
bar
: baz;
Вот что получается на выходе:
const str3 = foo
? bar
: baz;
После избавления от комментариев @ts-ignore мы запускаем плагин hoistClassStaticsPlugin [34]. Он проходится по всем объявлениям классов. Плагин определяет возможность поднятия идентификаторов или выражений и выясняет, поднята ли уже некая операция присвоения на уровень класса.
Для того чтобы обеспечить высокую скорость разработки и избежать вынужденных возвратов к предыдущим версиям проекта, мы снабдили каждый плагин [35] и ts-migrate [36] набором модульных тестов.
Плагин reactPropsPlugin [37], основанный на этом [38] замечательном инструменте, преобразует информацию о типах из формата PropTypes в объявления типов TypeScript. С помощью этого плагина нужно обрабатывать исключительно .tsx-файлы, содержащие хотя бы один React-компонент. Этот плагин ищет все объявления PropTypes и пытается разобрать их с использованием AST и простых регулярных выражений наподобие /number/, или с привлечением более сложных регулярных выражений вроде /objectOf$/ [39]. Когда обнаруживается React-компонент (функциональный, или основанный на классах), он трансформируется в компонент, в котором для входных параметров (props) используется новый тип: type Props = {…};.
Плагин reactDefaultPropsPlugin [40] отвечает за реализацию в React-компонентах паттерна defaultProps [41]. Мы используем особый тип, представляющий входные параметры, которым заданы значения, применяемые по умолчанию:
type Defined<T> = T extends undefined ? never : T;
type WithDefaultProps<P, DP extends Partial<P>> = Omit<P, keyof DP> & {
[K in Extract<keyof DP, keyof P>]:
DP[K] extends Defined<P[K]>
? Defined<P[K]>
: Defined<P[K]> | DP[K];
};
Мы пытаемся найти входные параметры, которым назначены значения, применяемые по умолчанию, после чего объединяем их с типом, описывающим входные параметры компонента, созданным на предыдущем шаге.
В экосистеме React широко применяются концепции [42] состояния и жизненного цикла компонентов. Мы решаем задачи, относящиеся к этим концепциям, в следующей паре плагинов. Так, если компонент имеет состояние, то плагин reactClassStatePlugin [43] генерирует новый тип (type State = any;), а плагин reactClassLifecycleMethodsPlugin [44] аннотирует методы жизненного цикла компонента соответствующими типами. Функционал этих плагинов может быть расширен, в том числе — за счёт оснащения их возможностью заменять any на более точные типы.
Эти плагины можно улучшать, в частности, за счёт расширения поддержки типов для состояния и свойств. Но и их существующие возможности, как оказалось, являются хорошей отправной точкой для реализации нужного нам функционала. Мы, кроме того, не работаем тут с React-хуками [45], та как в начале миграции в нашей кодовой базе использовалась старая версия React, не поддерживающая хуки.
Наша цель заключается в том, чтобы TypeScript-проект, оснащённый базовыми типами, скомпилировался бы, и чтобы при этом не изменилось бы поведение программы.
После всех трансформаций и модификаций наш код может оказаться неоднородно отформатированным, что способно привести к тому, что некоторые проверки кода линтером выявят ошибки. В кодовой базе нашего фронтенда используется система, основанная на Prettier и ESLint. А именно, Prettier [46] применяется для автоматического форматирования кода, а ESLint [47] помогает проверять код на предмет его соответствия рекомендованным подходам к разработке. Всё это позволяет нам быстро справляться с проблемами форматирования кода, появившимися в результате ранее выполненных действий, просто воспользовавшись соответствующим плагином [48] — eslintFixPlugin.
Последним шагом конвейера миграции является проверка того, что решены все проблемы компиляции TypeScript-кода. Для того чтобы находить и исправлять потенциальные ошибки плагин tsIgnorePlugin [49] берёт сведения семантической диагностики кода и номера строк, а после этого добавляет в код комментарии @ts-ignore с объяснениями ошибок. Например, это может выглядеть так:
// @ts-ignore ts-migrate(7053) FIXME: No index signature with a parameter of type 'string...
const { field1, field2, field3 } = DATA[prop];
// @ts-ignore ts-migrate(2532) FIXME: Object is possibly 'undefined'.
const field2 = object.some_property;
Мы оснастили систему и поддержкой синтаксиса JSX:
{*
// @ts-ignore ts-migrate(2339) FIXME: Property 'NORMAL' does not exist on type 'typeof W... */}
<Text weight={WEIGHT.NORMAL}>
some text
</Text>
<input
id="input"
// @ts-ignore ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'string'.
name={getName()}
/>
То, что в нашем распоряжении имеются осмысленные сообщения об ошибках, упрощает исправление ошибок и поиск фрагментов кода, на которые нужно обратить внимание. Соответствующие комментарии, в комбинации с $TSFixMe, позволяют нам собирать ценные данные о качестве кода и находить потенциально проблемные фрагменты кода. $TSFixMe — это созданный нами псевдоним для типа any. А для функций это — $TSFixMeFunction = (…args: any[]) => any;. Рекомендуется избегать использования типа any, но его применение помогло нам упростить процесс миграции. Использование этого типа помогало нам точно узнавать о том, какие именно фрагменты кода нуждаются в доработке.
Стоит отметить, что плагин eslintFixPlugin запускается два раза. Первый раз — до применения tsIgnorePlugin, так как форматирование способно подействовать на сообщения о том, где именно происходят ошибки компиляции. Второй раз — после применения tsIgnorePlugin, так как добавление в код комментариев @ts-ignore может привести к появлению ошибок форматирования.
Мы хотели бы обратить ваше внимание на пару особенностей миграции, которые мы заметили в ходе работы. Возможно, вам знание об этих особенностях пригодится при работе с вашими проектами.
@ts-expect-error необходимости нет. В кодовой базе Airbnb осуществлён переход с комментариев @ts-ignore на комментарии @ts-expect-error.
Миграция кодовой базы Airbnb с JavaScript на TypeScript всё ещё продолжается. У нас есть некоторые старые проекты, которые всё ещё представлены JavaScript-кодом. В нашей кодовой базе всё ещё часто встречаются $TSFixMe и комментарии @ts-ignore.

JavaScript и TypeScript в Airbnb
Но нужно отметить, что применение ts-migrate очень сильно ускорило процесс перевода наших проектов с JS на TS и сильно улучшило продуктивность нашего труда. Благодаря ts-migrate программисты смогли сосредоточить усилия на улучшении типизации, а не на ручной обработке каждого файла. В настоящее время примерно 86% нашего фронтенд-монорепозитория, в котором имеется около 6 миллионов строк кода, переведено на TypeScript. Мы, к концу этого года, ожидаем достичь показателя в 95%.
Здесь [52], на главной странице репозитория проекта, вы можете узнать о том, как установить и запустить ts-migrate. Если вы найдёте в ts-migrate какие-то проблемы [53], или если у вас будут идеи по улучшению этого инструмента — приглашаем вас присоединиться [54] к работе над ним!
Доводилось ли вам переводить большие проекты с JavaScript на TypeScript?
Автор: ru_vds
Источник [56]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka/356613
Ссылки в тексте:
[1] здесь: https://www.youtube.com/watch?v=P-J9Eg7hJwE
[2] Image: https://habr.com/ru/company/ruvds/blog/517312/
[3] allowJS: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#--declaration-and---allowjs
[4] codemods: https://medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb
[5] React: https://reactjs.org/
[6] jscodeshift: https://github.com/facebook/jscodeshift
[7] ts-migrate: https://github.com/airbnb/ts-migrate/tree/master/packages/ts-migrate
[8] ts-migrate-server: https://github.com/airbnb/ts-migrate/tree/master/packages/ts-migrate-server
[9] ts-migrate-plugins: https://github.com/airbnb/ts-migrate/tree/master/packages/ts-migrate-plugins
[10] migration: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate/cli.ts#L99
[11] reignore: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate/cli.ts#L174
[12] TSServer: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-server/src/forkTSServer.ts
[13] используется: https://github.com/Microsoft/vscode/blob/dfafad3a00f02469b644c76613d08716b8b31d8d/extensions/typescript-language-features/src/tsServer/server.ts#L139
[14] языковой протокол: https://microsoft.github.io/language-server-protocol/
[15] Средство для выполнения миграции: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-server/src/migrate/index.ts#L16
[16] Разбор: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-server/src/migrate/index.ts#L19
[17] Создание: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-server/src/migrate/index.ts#L54
[18] Отправка: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-server/src/migrate/index.ts#L103
[19] Обработка: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-server/src/migrate/index.ts#L135
[20] обновляем содержимое: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-server/src/migrate/index.ts#L147
[21] examples: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-example/src/index.ts#L19
[22] main: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate/cli.ts#L96
[23] примеры плагинов: https://github.com/airbnb/ts-migrate/tree/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-example/src
[24] Плагины: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-example/src/example-plugin-jscodeshift.ts
[25] Плагины: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-example/src/example-plugin-ts.ts
[26] Плагины: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-example/src/example-plugin-text.ts
[27] конвейера миграции: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-example/src/index.ts#L18
[28] recast: https://github.com/benjamn/recast
[29] explicitAnyPlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/explicit-any.ts
[30] declareMissingClassPropertiesPlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/declare-missing-class-properties.ts
[31] значит: https://github.com/microsoft/TypeScript/blob/20ecbb0f46105ccaead2970f6ef23188955e023e/src/compiler/diagnosticMessages.json#L1348-L1351
[32] ES6-классам: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
[33] stripTSIgnorePlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/strip-ts-ignore.ts
[34] hoistClassStaticsPlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/hoist-class-statics.ts
[35] плагин: https://github.com/airbnb/ts-migrate/tree/master/packages/ts-migrate-plugins/tests/src
[36] ts-migrate: https://github.com/airbnb/ts-migrate/tree/master/packages/ts-migrate-server/tests/commands/migrate
[37] reactPropsPlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/react-props.ts
[38] этом: https://github.com/lyft/react-javascript-to-typescript-transform
[39] /objectOf$/: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/utils/react-props.ts#L237
[40] reactDefaultPropsPlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/react-default-props.ts
[41] defaultProps: https://reactjs.org/docs/typechecking-with-proptypes.html
[42] концепции: https://reactjs.org/docs/state-and-lifecycle.html
[43] reactClassStatePlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/react-class-state.ts
[44] reactClassLifecycleMethodsPlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/react-class-lifecycle-methods.ts
[45] React-хуками: https://reactjs.org/docs/hooks-intro.html
[46] Prettier: https://prettier.io/
[47] ESLint: https://eslint.org/
[48] плагином: https://github.com/airbnb/ts-migrate/blob/fe1b6021783b1ef5c4b8fa310b96a44779cc77bd/packages/ts-migrate-plugins/src/plugins/eslint-fix.ts
[49] tsIgnorePlugin: https://github.com/airbnb/ts-migrate/blob/e163ea39a8bd62105773625236f9b4098883c4f3/packages/ts-migrate-plugins/src/plugins/ts-ignore.ts
[50] @ts-nocheck: https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/#ts-nocheck-in-typescript-files
[51] @ts-expect-error: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#-ts-expect-error-comments
[52] Здесь: https://github.com/airbnb/ts-migrate
[53] проблемы: https://github.com/airbnb/ts-migrate/issues
[54] присоединиться: https://github.com/airbnb/ts-migrate/blob/master/CONTRIBUTING.md
[55] Image: http://ruvds.com/ru-rub?utm_source=habr&utm_medium=perevod&utm_campaign=ts-migrate-a-tool-for-migrating-to-typescript-at-scale
[56] Источник: https://habr.com/ru/post/517312/?utm_source=habrahabr&utm_medium=rss&utm_campaign=517312
Нажмите здесь для печати.