В какой-то момент борьбы со Flow-Type на VSCode, я согласился, что нужно переезжать на TypeScript. Поддержка Flow-Type обеспечивается сторонним плагином и совсем-совсем не устраивает. Если файл невалиден с точки зрения Flow-Type, то переходы внутри кода между файлами перестают работать, например. А возвращаться на WebStorm после знакомства с VSCode — я не могу себя заставить. Microsoft, как обычно, затягивает полностью. Любишь VSCode, получи TypeScript.
Если бы мне кто сказал год назад, что я вернусь в поклонники Microsoft — сложно было такое представить. Но случаются и более удивительные вещи. Я в полном восторге от качества китайского набора React-компонентов от Ant-Design. И хотя он написан на TypeScript, чтобы его прикрурить, нужен babel-plugin-import.
Но как же остаться на Create React App (CRA) — у форка для TypeScript (CRA-TS) выпилили Babel. Поддерживать собственную вариацию CRA представляется безумием. Многообещающий Preact-CLI (как замена CRA) не обеспечивает необходимый уровень совместимости с React. Но, играясь с Preact-CLI, заметил, что preact.config.js очень похож на react-app-rewired, которым я активно пользуюсь для обхода ограничений конфигурации Webpack в CRA. Сопоставил этот факт с идеей перевода CRA-TS c ts-loader на awesome-typescript-loader, внутри которого можно включить Babel. И вуаля!
0) установить create-react-app
$ npm install -g create-react-app
1) создать проект
$ create-react-app cra-ts-antd --scripts-version=react-scripts-ts $ cd cra-ts-antd/
2) добавить пакеты
$ yarn add react-app-rewired react-app-rewire-less awesome-typescript-loader babel-core babel-plugin-import babel-preset-react-app -D
3) добавить config-overrides.js
module.exports = function override(config, env) { const tsLoader = config.module.rules.find(conf => { return conf.loader && conf.loader.includes('ts-loader') }) tsLoader.loader = require.resolve('awesome-typescript-loader') tsLoader.query = { useBabel: true, } const tsLintLoader = config.module.rules.find(conf => { return conf.loader && conf.loader.includes('tslint-loader') }) tsLintLoader.options = tsLintLoader.options || {} // FIXED Warning: The 'no-use-before-declare' rule requires type infomation. tsLintLoader.options.typeCheck = true const rewireLess = require('react-app-rewire-less') config = rewireLess(config, env) const path = require('path') // For import with absolute path config.resolve.modules = [path.resolve('src')].concat(config.resolve.modules) return config }
4) изменить package.json; код подключает враппер react-app-rewired
"scripts": { - "start": "react-scripts-ts start", - "build": "react-scripts-ts build", + "start": "BROWSER=none react-app-rewired start --scripts-version react-scripts-ts", + "build": "react-app-rewired build --scripts-version react-scripts-ts", }
5) изменить tsconfig.json; код включает настройки для абсолютного импорта, помимо прочего
{ "compilerOptions": { + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "*": ["*", "src/*"] + }, - "jsx": "react", + "jsx": "preserve", }, "exclude": [ + "config-overrides.js", ] }
6) добавить .babelrc; код назначает требуемый пресет и подключает babel-plugin-import
{ "presets": ["react-app"], "plugins": [ ["import", { "libraryName": "antd", "style": false }] ] }
7) добавить antd; версия фиксированная, т.к. в следующей версии 2.12.3 обнаружена ошибка
$ yarn add antd@2.12.2
8) добавить src/resources/main.less; код переопределяет переменную
@import "~antd/dist/antd.less"; // import official less entry file @primary-color: #1DA57A;
9)… и подключить в index.tsx; импорт по абсолютному пути от src
+ import 'resources/main.less';
10) изменить App.tsx
import * as React from 'react'; import './App.css'; + import { Button } from 'antd'; const logo = require('./logo.svg'); class App extends React.Component<{}, {}> { render() { return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2>Welcome to React</h2> </div> <p className="App-intro"> To get started, edit `src/App.tsx` and save to reload. </p> + <Button type="primary">Test</Button> </div> ); } } export default App;