Here's every step to creating a React Native app (with or without Expo), written in and type checked with TypeScript, but compiled with Babel 7.
You're assumed to have some proficiency with using the terminal.
I'm on a Mac, but the steps shouldn't be much different on Ubuntu Linux or Windows.
- Install Homebrew.
It's a package manager for Macs, and Linux too, but if you're on Ubuntu or Debian, I suggest just use the built-in one like APT. - Install Node thru brew.
Installing Node should include the NPM package manager with it, which is needed below. - Some say to next install Yarn thru brew.
Yarn is yet another node package manager... you can skip this if you want, it's not strictly needed, and I won't use Yarn in this tutorial. - Fix NPM if you installed Yarn.
I actually mentioned Yarn at all only because with Homebrew, you might now need to fix the Node NPM install as it might be broken by installing yarn. Simply[1] run:yarn global add npm
. Remember, we don't need Yarn in this tutorial though. - Install Expo thru npm:$
npm install -g expo-cli
Expo is apparently kind of a mini-platform for running React Native apps, and makes it easy to test a React Native app on devices through its Expo app, or on Macs and PCs without resorting to an Android or iOS emulator (which is great, because those emulators are dreadfully slow in my experience). Plus installing Expo should include React Native with it. - Install React Native CLI thru npm:$
npm install -g react-native-cli
This is basically a template that creates a RN project for you to start from.
New Project with React Native or Expo
1. Create a React Native project (possibly thru Expo):
$ expo init CoolProject
or alternatively without Expo:$ react-native init CoolProject
1.5. Upgrade core-js:
I got an error like this:
warning react-native > create-react-class > fbjs > core-js@1.2.7: core-js@<2.6.8 is no longer maintained. Please, upgrade to core-js@3 or at least to actual version of core-js@2.
You just need to upgrade
core-js
. Go into your CoolProject
folder and run:npm install --save core-js@^3
2.
cd CoolProject
to go into the new project's root directory, then install Babel:$ npm install --save-dev @babel/core @babel/cli
I assume this will install for you Babel 7 or above. We'll need it later to migrate to TypeScript.
3. Create a
So instead, we're going to reserve "src" for later when we migrate to TypeScript, instead of calling it "src" right now when we're still dealing with just JavaScript JS, or JSX, files.
Separating the TypeScript and the compiled JavaScript files is a technique to avoid in-source builds (a usual technique used in compiled languages like C++: see e.g. in-source vs out-of-source builds).
4. Adjust the project's entry point to running your code.
Prior to creating the
Expo React Native apps are slightly different than plain React Native apps though:
3. Create a
lib
folder in the project root.$ mkdir lib
The lib
folder will be used to contain the App's JavaScript files. Traditionally, the folder would be called "src", but looking forward, these JavaScript files eventually will be produced by the TypeScript compiler for us.So instead, we're going to reserve "src" for later when we migrate to TypeScript, instead of calling it "src" right now when we're still dealing with just JavaScript JS, or JSX, files.
Separating the TypeScript and the compiled JavaScript files is a technique to avoid in-source builds (a usual technique used in compiled languages like C++: see e.g. in-source vs out-of-source builds).
4. Adjust the project's entry point to running your code.
Prior to creating the
lib
folder to contain the App's JavaScript files, React Native projects would go looking for your programming code via an index.js
, or App.js
file. This needs an adjustment given we want to contain everything in the lib
directory.Expo React Native apps are slightly different than plain React Native apps though:
- Expo has no platform native specific code, so there's no
index.js
file to speak of. InsteadApp.js
is the entry point and it's essentially hard coded into the Expo framework. So instead[2], create aMain.js
file inlib
and haveApp.js
essentially point there. - Thus App.js should look like this:
import React from 'react';
import Main from './lib/Main';
const App = () => (
<main />
);
export default App;
- And lib/Main.js is like:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class Main extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Modify me!</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
- For plain React Native projects (i.e. created with
react-native init CoolProj
instead of the expo equivalent), there should be anindex.js
file. In it there's code to registerApp.js
as the non-native cross-platform JavaScript entry point. Just move the existingApp.js
file into thelib
folder and modifyindex.js
to point to./lib/App.js
instead.
I assume Babel 7 or above was installed already, because it means we can just use Babel to compile the TypeScript for us. We'll use the actual TypeScript compiler purely for type checking later.
1. Install the Babel TypeScript presets, etc:
From within the project's root directory:
$ npm install --save-dev @babel/preset-typescript @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread
2. Set up
.babelrc
in the project root with at least the following in it:{
"presets": [
"@babel/env",
"@babel/preset-typescript"
],
"plugins": [
"@babel/proposal-class-properties",
"@babel/proposal-object-rest-spread"
]
}
lib
folder's name to src
.4. Migrate JavaScript files to TypeScript file extensions
Go into the
src
folder and change all the file extensions from .jsx
to .tsx
, and from .js
to .ts
.5. Compile your project:
$ npx babel ./src --out-dir lib --extensions ".ts,.tsx"
You should now have a lib
directory of JavaScript files compiled from the TypeScript files from the src
folder.Because we started with a
lib
folder previously, prior to migrating to TypeScript, there's now no need to modify App.js
or index.js
to point to lib
, since it was pointing there all along (nice bit of foresight)!In the
package.json
file, add the following "compile" line:
...
"scripts": {
...
"compile": "babel ./src --out-dir lib --extensions '.ts,.tsx'"
...}
Now you can easily compile using:
npm run compile
Caveat - there's 4 TypeScript features that Babel won't compile:
- namespaces: Just use standard ES6 modules (
import
/export
) instead - Type casting like
<NewType>x
when JSX is enabled anywhere. Just writex as NewType
instead. - enums that span multiple declarations. So not open ended then.
- legacy-style import/export syntax: e.g.
import foo = require(...)
orexport = foo
Setup TypeScript for Type Checking
1. Install TypeScript compiler.
$ npm install --save-dev typescript
2. Install TypeScript type definitions for React Native
Install[3] type definitions and declarations for React, the test framework Jest, etc.
$ npm install --save-dev @types/jest @types/react @types/react-native @types/react-test-renderer
3. Set up
tsconfig.json
in the project root with at least the following in it:{
"compilerOptions": {
"target": "esnext",
"moduleResolution": "node",
"allowJs": true,
"noEmit": true,
"strict": true,
"isolatedModules": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "react-native"
},
"include": [
"./src"
],
"exclude": [
"./node_modules"
]
}
There's lots of compiler options. Read more about "jsx" and "allowSyntheticDefaultImports" if you want.
4. Try type checking the project by running
npx tsc.
Fix duplicate types error if needed.If there's no errors, then great! I got a lot of duplicate type definition errors though. It's because there are duplicate types defined for React Native conflicting with the default definitions provided by TypeScript's definition files for the
dom
etc.There's two ways to fix this duplicate types error:
- (1) The better, preferable option in my mind is to go in
tsconfig.json
, and add this"lib"
compiler option:
"compilerOptions": {
...
"lib": ["esnext"],
...
}
}
The "lib" compiler option by default also include the
dom
type definitions from the TypeScript compiler. Specifying it here like so means it won't be included, thus won't conflict with the React Native type definitions (according to Typescript: Duplicated identifier between React Native and ES6 Lib).- (2) A much more drastic, draconian way to fix it is to go inside
tsconfig.json
, and add the"skipLibCheck"
compiler option:
{
"compilerOptions": {
...
"skipLibCheck": true
...
}
}
It's draconian because this option makes the TypeScript compiler skip type checking all type declaration files. It's kind of a nuclear option to fixing the problem (see error TS2300: Duplicate identifier 'RequestInfo' Ask)5. Make the type checking command into a script:
In the
package.json
file, add the following "tsc" line:...
"scripts": {
...
"tsc": "npx tsc"
...}
Now you can use TypeScript compiler to check types by either
npx tsc
or with npm run tsc
.6. Adjust type checking options to taste:
See: Unofficial React Native TypeScript
More TypeScript compiler options: TypeScript docs: Compiler Options
Future Work
Use a test framework, like Jest.
Learn more Mobile App Development with React Native (CSCI E-39b, produced by CS50)
References
I learned from many others, but oddly most only write on one aspect or another but don't put the whole thing together from top to bottom like I did above. Thanks to the following though!
[3] TypeScript With Babel: A Beautiful Marriage
[4] Microsoft/TypeScript-React-Native-Starter
[5] React Native Blog: Using TypeScript with React Native
Note this seems like a full copy pasta of the above Microsoft/TypeScript-React-Native-Starter read me page, or vice versa, or written by the same person, or... I gave up trying to figure that out. But the blog post was awesome in pointing out the new TypeScript and Babel 7 support.
[6] Microsoft TypeScript blog: TypeScript and Babel 7
[7] React Native Docs: Getting Started
No comments:
Post a Comment