{"id":13602541,"url":"https://github.com/microsoft/TypeScript-React-Conversion-Guide","last_synced_at":"2025-04-11T08:32:57.459Z","repository":{"id":38360150,"uuid":"90787694","full_name":"microsoft/TypeScript-React-Conversion-Guide","owner":"microsoft","description":"A guide for converting a simple JavaScript/React project to TypeScript. Contains both before an after code with the step-by-step process in the README below.","archived":true,"fork":false,"pushed_at":"2021-02-01T20:31:20.000Z","size":491,"stargazers_count":1431,"open_issues_count":10,"forks_count":208,"subscribers_count":39,"default_branch":"master","last_synced_at":"2025-04-08T10:12:23.739Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/microsoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-09T20:12:33.000Z","updated_at":"2025-04-03T11:18:24.000Z","dependencies_parsed_at":"2022-08-09T03:15:23.021Z","dependency_job_id":null,"html_url":"https://github.com/microsoft/TypeScript-React-Conversion-Guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2FTypeScript-React-Conversion-Guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2FTypeScript-React-Conversion-Guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2FTypeScript-React-Conversion-Guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2FTypeScript-React-Conversion-Guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/microsoft","download_url":"https://codeload.github.com/microsoft/TypeScript-React-Conversion-Guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248361678,"owners_count":21090962,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T18:01:26.903Z","updated_at":"2025-04-11T08:32:52.448Z","avatar_url":"https://github.com/microsoft.png","language":"JavaScript","readme":"# TypeScript React Conversion Guide\r\n\r\nThis walkthrough illustrates how to adopt TypeScript in an existing React/Babel/Webpack project. We'll start with a TicTacToe project written fully in JavaScript in the `TicTacToe_JS` folder as an example. By the end, you will have a TicTacToe project fully written with TypeScript.\r\n\r\nIf you are starting a new React project instead of converting one, you can use [this tutorial](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/tutorials/React.md).\r\n\r\nAdopting TypeScript in any project can be broken down into 2 phases:\r\n\r\n* Adding the TypeScript compiler (tsc) to your build pipeline.\r\n* Converting JavaScript files into TypeScript files.\r\n\r\n## Understand the existing JavaScript project\r\n\r\nBefore we dive into TypeScript adoption, let's take a look at the structure of the TicTacToe app -- it contains a few components and looks like the below (with or without TypeScript).\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src =\"image/components.png\"/\u003e\r\n\u003c/p\u003e\r\n\r\nAs shown in `package.json`, the app already includes React/ReactDOM, Webpack as the bundler \u0026 task runner, and the [babel-loader](https://github.com/babel/babel-loader) Webpack plugin to use Babel for ES6 and JSX transpilation. The project initially has the below overall layout before we adopt TypeScript:\r\n\r\n```txt\r\nTicTacToe_JS /\r\n    |---- css/\t\t\t              // css style sheets\r\n    |---- src/\t\t\t              // source files\r\n        |---- app.jsx\t\t          // the App React component\r\n        |---- board.jsx\t\t        // the TicTacToe Board React component\r\n        |---- constants.js\t\t    // some shared constants\r\n        |---- gameStateBar.jsx\t  // GameStatusBar React component\r\n        |---- restartBtn.jsx\t    // RestartBtn React component\r\n    |---- .babelrc\t\t            // a list of babel presets\r\n    |---- index.html\t\t          // web page for our app\r\n    |---- package.json\t\t        // node package configuration file\r\n    |---- webpack.config.js\t      // Webpack configuration file\r\n```\r\n\r\n## Add TypeScript compiler to build pipeline\r\n\r\n### Install dependencies\r\n\r\nTo get started, open a terminal and `cd` to the `TicTacToe_JS` folder. Install all dependencies defined in `package.json`.\r\n\r\n```sh\r\ncd TicTacToe_JS\r\nnpm install\r\n```\r\n\r\nAdditionally, install TypeScript (3 or higher), [ts-loader](https://www.npmjs.com/package/ts-loader) and [source-map-loader](https://www.npmjs.com/package/source-map-loader) as dev dependencies if you haven't. ts-loader is a Webpack plugin that helps you compile TypeScript code to JavaScript, much like babel-loader for Babel. There are also other alternative loaders for TypeScript! Source-map-loader adds source map support for debugging.\r\n\r\n```sh\r\nnpm install --save-dev typescript ts-loader source-map-loader\r\n```\r\n\r\nGet the type declaration files (.d.ts files) from [@types](https://blogs.msdn.microsoft.com/typescript/2016/06/15/the-future-of-declaration-files/) for any library in use. For this project, we have React and ReactDOM.\r\n\r\n```sh\r\nnpm install --save @types/react @types/react-dom\r\n```\r\n\r\nIf you are using an older version of React or ReacDOM that is incompatible with the latest .d.ts files from @types, you can specify a version number for `@types/react` or `@types/react-dom` in `package.json`.\r\n\r\n### Configure TypeScript\r\n\r\nNext, configure TypeScript by creating a `tsconfig.json` file in the `TicTacToe_JS` folder, and add:\r\n\r\n```json5\r\n{\r\n    \"compilerOptions\": {\r\n        \"outDir\": \"./dist/\",        // path to output directory\r\n        \"sourceMap\": true,          // allow sourcemap support\r\n        \"strictNullChecks\": true,   // enable strict null checks as a best practice\r\n        \"module\": \"es6\",            // specify module code generation\r\n        \"jsx\": \"react\",             // use typescript to transpile jsx to js\r\n        \"target\": \"es5\",            // specify ECMAScript target version\r\n        \"allowJs\": true             // allow a partial TypeScript and JavaScript codebase\r\n\r\n    },\r\n    \"include\": [\r\n        \"./src/\"\r\n    ]\r\n}\r\n```\r\n\r\nYou can edit some of the options or add more based on your project's requirements. See more options in the full list of [compiler options](https://www.typescriptlang.org/docs/handbook/compiler-options.html).\r\n\r\n### Set up build pipeline\r\n\r\nTo add TypeScript compilation as part of our build process, you need to modify the Webpack config file `webpack.config.js`. This section is specific to Webpack. However, if you are using a different task runner (e.g. Gulp) for your React/Babel project, the idea is the same - replace the Babel build step with TypeScript, as TypeScript also offers transpiling to lower ECMAScript versions and JSX transpilation with a shorter build time in most cases. If you wish, you can also keep Babel by adding a TypeScript build step before Babel and feeding its output to Babel.\r\n\r\nGenerally, we need to change `webpack.config.js` in a few ways:\r\n\r\n1. Expand the module resolution extensions to include `.ts` and `.tsx` files.\r\n2. Replace `babel-loader` with `ts-loader`.\r\n3. Add source-map support.\r\n\r\nLet's modify `webpack.config.js` with the below:\r\n\r\n```js\r\nmodule.exports = {\r\n  // change to .tsx if necessary\r\n  entry: './src/app.jsx',\r\n  output: {\r\n    filename: './bundle.js'\r\n  },\r\n  resolve: {\r\n    // changed from extensions: [\".js\", \".jsx\"]\r\n    extensions: [\".ts\", \".tsx\", \".js\", \".jsx\"]\r\n  },\r\n  module: {\r\n    rules: [\r\n      // changed from { test: /\\.jsx?$/, use: { loader: 'babel-loader' }, exclude: /node_modules/ },\r\n      { test: /\\.(t|j)sx?$/, use: { loader: 'ts-loader' }, exclude: /node_modules/ },\r\n\r\n      // addition - add source-map support\r\n      { enforce: \"pre\", test: /\\.js$/, exclude: /node_modules/, loader: \"source-map-loader\" }\r\n    ]\r\n  },\r\n  externals: {\r\n    \"react\": \"React\",\r\n    \"react-dom\": \"ReactDOM\",\r\n  },\r\n  // addition - add source-map support\r\n  devtool: \"source-map\"\r\n}\r\n```\r\n\r\nYou can delete `.babelrc` and all Babel dependencies from `package.json` if you no longer need them.\r\n\r\nNote that if you plan to adopt TypeScript in the entry file, you should change `entry: './src/app.jsx',` to `entry: './src/app.tsx',` as well. For the time being, we will keep it as `app.jsx`.\r\n\r\nYou now have the build pipeline correctly set up with TypeScript handling the transpilation. Try bundling the app with the following command and then open `index.html` in a browser:\r\n\r\n```sh\r\nnpx webpack\r\n```\r\n\r\n\u003e We are assuming you are using `npx` addition for `npm` to [execute npm packages directly](http://blog.npmjs.org/post/162869356040/introducing-npx-an-npm-package-runner)\r\n\r\n## Transition from JS(X) to TS(X)\r\n\r\nIn this part, we will walk through the following steps progressively,\r\n\r\n1. The minimum steps of converting one module to TypeScript.\r\n2. Adding types in one module to get richer type checking.\r\n3. Fully adopting TypeScript in the entire codebase.\r\n\r\nWhile you get the most out of TypeScript by fully adopting it across your codebase, understanding each of the three steps comes in handy as you decide what to do in case you have certain part of your JavaScript codebase you want to leave as-is (think legacy code that no one understands).\r\n\r\n### Minimum transition steps\r\n\r\nLet's look at `gameStateBar.jsx` as an example.\r\n\r\nStep one is to rename `gameStateBar.jsx` to `gameStateBar.tsx`. If you are using any editor with TypeScript support such as [Visual Studio Code](https://code.visualstudio.com/), you should be able to see a few complaints from your editor.\r\n\r\nOn line 1 `import React from \"react\";`, change the import statement to `import * as React from \"react\"`. This is because while importing a CommonJS module, Babel assumes `modules.export` as default export, while TypeScript does not.\r\n\r\nOn line 3 `export class GameStateBar extends React.Component {`, change the class declaration to `export class GameStateBar extends React.Component\u003cany, any\u003e {`. The type declaration of `React.Component` uses [generic types](https://www.typescriptlang.org/docs/handbook/generics.html) and requires providing the types for the property and state object for the component. The use of `any` allows us to pass in any value as the property or state object, which is not useful in terms of type checking but suffices as minimum effort to appease the compiler.\r\n\r\nBy now, ts-loader should be able to successfully compile this TypeScript component to JavaScript. Again, try bundling the app with the following command and then open `index.html` in a browser,\r\n\r\n```sh\r\nnpx webpack\r\n```\r\n\r\n### Add types\r\n\r\nThe more type information provided to TypeScript, the more powerful its type checking is. As a best practice, we recommend providing types for all declarations. We will again use the `gameStateBar` component as an example.\r\n\r\nFor any `React.Component`, we should properly define the types of the property and state object. The `gameStateBar` component has no properties, therefore we can use `{}` as type.\r\n\r\nThe state object contains only one property `gameState` which shows the game status (either nothing, someone wins, or draw). Given `gameState` can only have certain known string literal values, let's use [string literal type](https://www.typescriptlang.org/docs/handbook/advanced-types.html) and define the interface as follow before the class declaration.\r\n\r\n```ts\r\ninterface GameStateBarState {\r\n    gameState: \"\" | \"X Wins!\" | \"O Wins!\" | \"Draw\";\r\n}\r\n```\r\n\r\nWith the defined interface, change the `GameStateBar` class declaration,\r\n\r\n```ts\r\nexport class GameStateBar extends React.Component\u003c{}, GameStateBarState\u003e {...}\r\n```\r\n\r\nNow, supply type information for its members. Note that providing types to all declarations is not required, but recommended for better type coverage.\r\n\r\n```ts\r\n// add types for params\r\nconstructor(props: {}) {...}\r\nhandleGameStateChange(e: CustomEvent) {...}\r\nhandleRestart(e: Event) {...}\r\n\r\n// add types in arrow functions\r\ncomponentDidMount() {\r\n    window.addEventListener(\"gameStateChange\", (e: CustomEvent) =\u003e this.handleGameStateChange(e));\r\n    window.addEventListener(\"restart\", (e: CustomEvent) =\u003e this.handleRestart(e));\r\n}\r\n\r\n// add types in arrow functions\r\ncomponentWillUnmount() {\r\n    window.removeEventListener(\"gameStateChange\", (e: CustomEvent) =\u003e this.handleGameStateChange(e));\r\n    window.removeEventListener(\"restart\", (e: CustomEvent) =\u003e this.handleRestart(e));\r\n}\r\n```\r\n\r\nTo use stricter type checking, you can also specify useful [compiler options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) in `tsconfig.json`. For example, `noImplicitAny` is a recommended option that triggers the compiler to error on expressions and declarations with an implied `any` type.\r\n\r\nYou can also add [private/protected modifier](https://www.typescriptlang.org/docs/handbook/classes.html) to class members for access control. Let's mark `handleGameStateChange` and `handleRestart` as `private` as they are internal to `gameStateBar`.\r\n\r\n```ts\r\nprivate handleGameStateChange(e: CustomEvent) {...}\r\nprivate handleRestart(e: Event) {...}\r\n```\r\n\r\nAgain, try bundling the app with the following command and then open `index.html` in a browser,\r\n\r\n```sh\r\nnpx webpack\r\n```\r\n\r\n### Adopt TypeScript in the entire codebase\r\n\r\nAdopting TypeScript in the entire codebase is more or less repeating the previous two steps for all js(x) files. You may need to make changes additional to what is mentioned above while converting perfectly valid JavaScript to TypeScript. However the TypeScript compiler and your editor (if it has TypeScript support) should give you useful tips and error messages. For instance, parameters can be optional in JavaScript, but in TypeScript all [optional parameter](https://www.typescriptlang.org/docs/handbook/functions.html) must be marked with `?`\r\n\r\nYou can see the fully converted TicTacToe project in the `TicTacToe_TS` folder. Build the app with,\r\n\r\n```sh\r\nnpm install\r\nnpx webpack\r\n```\r\n\r\nRun the app by opening `index.html`.\r\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmicrosoft%2FTypeScript-React-Conversion-Guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmicrosoft%2FTypeScript-React-Conversion-Guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmicrosoft%2FTypeScript-React-Conversion-Guide/lists"}