Typescript, React Native and Sourcemaps

Posted by Parashuram on September 20, 2016

As the first article on a site intended to share experiments, best practices and insights learned by React Native developers at Microsoft, it’s only fitting that our first article marries React Native with TypeScript, Microsoft’s typed superset of JS.

ReactJS is already used by many, many teams at Microsoft. It’s been a natural transition for many of us — myself included — to move from web to “JS Native” mobile development using React Native. The virtues of native platform UI, dynamic updates and a sane app architecture (thank you, Facebook!) make it irresistable. But given recent conversations on the React messageboard, it’s also clear that many developers want the freedom to choose Typescript over Flow and/or Babel. While both the TS and Flow ecoystems bring many of the same benefits (e.g. ES-tomorrow in an ES-today world, greater confidence in code quality, higher productivity), some developers simply prefer TypeScript’s larger community, choice of tools or development philosophy.

Google Trends results for TypeScript, ES6 and Flow

React Native applications are typically written using JavaScript or JSX today. This blog post explains how to use TypeScript to author React Native projects.

Take a look at the sample project on github for details and to use as a starter kit.

Project Layout

For TypeScript, our recommendation is to place all the .tsx files under a dedicated sources folder so that you easily .gitignore the output folder and avoid committing compiled code. Different files for components, containers, actions, etc. could follow any conventions you already use. The tsconfig.json file will point to this source folder as the rootDir.

Folder Tree Structure

The tsc compiler can then place the converted .js files and sourcemaps in an output or bin folder. Remember to add the output folder to .gitignore. The tsconfig.json file should also be configured to emit sourcemaps, understand JSX and include/exclude the appropriate files.

A React Native application starts at the entry points in the index.android.js and index.ios.js for the respective platforms. While it is possible to change this to point to any file by editing the platforms folder, we recommend the pattern of keeping these file as lean as possible and moving the top level component into the sources folder, say src/App.jsx. Alternatively, the entry files can also be mirrored with corresponding index.android.tsx and index.ios.tsx that would contain the top level components for the respective platforms, as in the example repository.

Type definitions for components

Fetching type definitions and configuring them in a React Native project is no different from the workflow in other TypeScript projects. The typings tool is probably the most common way to fetch type definations for third party components that are written in JavaScript. If the typings tool is used, ensure that a typings.json is created and libraries like react and react-native are added as global dependencies. For components that may not have a type defination, the .d.ts files could be placed alongside the sources. If TypeScript 2.0 is used and the types are fetched using npm from the @types organization, the typings will be in node_modules.

Enabling Sourcemaps

The most common problem with using TypeScript is the lack of sourcemaps when debugging the React Native application. Most workflows today place breakpoints in files that are generated by the tsc compiler. To mitigate this problem, we can use the react-native-sm-transformer module. When starting a React Native packager using react-native start, the default Babel transformer can be overridden using the --transformer flag. The default transformer has a signature that looks like the following


function transform(src: string, filename: string, options: any): {
      ast: any,
      code: string,
      map: string,
      filename: string,
    };

The react-native-sm-transformer simply adds another transformer that appends any intermediate sourcemaps alongside the generated JavaScript files to the final bundle.

Thus, using react-native start --transformer node_modules/react-native-sm-transformer would enable sourcemaps for TypeScript. An additional --skipflow option may be passed since TypeScript is already helping with type checking.

Adding TSC to transformer pipeline

In an ideal scenario, tsc should be added to the transformation pipeline. However, React Native’s file watcher only picks up ['js', 'json'] files. Even if tsc is added as a transformer, the .ts files will not be picked up as a result. This also results in the source files to be transformed twice - once from src/*.ts to out/*.js by tsc and again from out/*.js to *.js by Babel.

The React Native repository has an issue that enables customizing the files patterns that are picked up. We plan to submit a pull request to resolve this issue. Once the issue is fixed, we could create a custom, TypeScript transformer for React Native and update this post to reflect the changes.

Next steps

You can try out Typescript by cloning the sample project on github on github, or just looking at the tsconfig.json and package.json and implementing them into your own project. If you are already using VSCode to take advantage of the excellent TypeScript support, the React Native extension for VSCode could add in support for in-editor debugging.