Convert flowed-project to TypeScript
Why Typescript?
First, I do not recommend everyone to move to TypeScript. Who’s being happy with Flow, it’s fine. I used Flow for my projects for a while and it’s still a powerful tool for type checking to prevent human mistake while developing process.
The main reason is that I want to give TypeScript a shot for my side project, it’s a trend for style checking now. TypeScript has team support behind, not like Flow and more tools or projects already integrated TypeScript nowaday: Angular, React, Vue, Babel 7, etc.

Handle Project
You can find my public project here and the pull request I made from flow-to-typescript
branch as well to see what I changed clearly. I created from create-react-app
(CRA) version 1.5 and I applied Flow type for it in the beginning. Now I’ll try to notes what I have done when converting my project from Flow to TypeScript.
Install TypeScript
First, we need to install typescript library and its dependencies
$ npm install --save-dev typescript @types/node @types/react @types/react-dom @types/react-router-dom @types/jest @types/moment @types/webpack-env
Configuration File
Second, we’ll create a configuration file name tsconfig.jsonfor TypeScript in our root project folder.
{
"compilerOptions": {
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2018", "dom"], /* Specify library files to be included in the compilation. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
/* Strict Type-Checking Options */
"strict": false, /* Enable all strict type-checking options. */
"strictNullChecks": true, /* Enable strict null checks. */
/* Additional Checks */
"noUnusedLocals": true, /* Report errors on unused locals. */
"noUnusedParameters": true, /* Report errors on unused parameters. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"baseUrl": ".", /* Base directory to resolve non-absolute module names. */
"paths": {
"@/*": ["./src/*"],
"screens/*": ["./src/screens/*"],
"components/*": ["./src/components/*"],
"assets/*": ["./src/assets/*"]
},
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
},
"exclude": ["node_modules"]
}
I already comment options in configuration files. You can find more in the official document.
Update File Names
Next stuff is we need to convert all file *.js
to *.tsx
for React Component (including test file for React component), or to *.ts
for utility or support files.
Remove all // @flow
annotations in which files we used it before.
Update Babel Configuration
{
"presets": [
"@babel/preset-typescript",
"@babel/preset-env",
"@babel/preset-react",
"react-app",
],
"plugins": [
[
"module-resolver", {
"alias": {
"@": "./src",
"screens": "./src/screens",
"components": "./src/components",
"assets": "./src/assets"
}
}
]
]
}
Please note here, I have to update module-resolver
configuration a little bit to work with import relative modules. In the past, when I worked with Flow, I only need to define module-resolver
configuration such as "root": ["./src"]
and it worked fine with relative path import. However, with TypeScript, I have to declare every relative path I use in project, and it have to be the same as baseUrl
and paths
in TypeScript configuration file above.
Adapt TypeScript Checker
From this step, everything I need to do is adapt type in my code to pass TypeScript checker.
We can add one more script, for example, typescript
in package.json
so that we can run TypeScript checking before we run unit test.
"scripts": {
"precommit": "NODE_ENV=production lint-staged",
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"predeploy": "npm run build",
"deploy": "aws s3 sync build/ s3://fx-position-app",
"typescript": "tsc",
"test": "TZ=Asia/Ho_Chi_Minh jest --testPathPattern=__tests__ --detectOpenHandles",
"testall": "eslint . && typescript && npm run test",
"lint": "eslint ."
},
Notes
I’ll take note some experience when working with TypeScript the first time.
Prefer Interface to Type
According to my friend recommend, I use interface instead of type. You can research more about interface vs type topics, for example this one https://www.educba.com/typescript-type-vs-interface/.
Before
type State = {
timeOption: string,
finishRingBell: boolean
};
After
interface IState {
timeOption: string
finishRingBell: boolean
}
Moment Type
Before
// @flow
import type { Moment } from 'moment';
type State = {
currentTime: Moment
};
After
import { Moment } from 'moment';
interface State {
currentTime: Moment
};
Non-null assertion operator
This is a new concept when I work with TypeScript. It tells the compiler that our object is not null. Let see an example:
Before
type State = {
lots: ?number
};
class PositionCalculator extends React.Component<{}, State> {
state = {
lots: null
}
calculate = () => {
this.setState({ lots: 1 });
}
render() {
<div>
<button className="btn btn-primary" onClick={this.calculate}>
Calculate
</button>
<div className="col-md-4">
<h5 className="card-title">Result</h5>
<label>Lots: {lots ? lots.toFixed(3) : 'N/A'}</label>
</div>
</div>
}
}
After
interface IState = {
lots: null | number
};
class PositionCalculator extends React.Component<{}, IState> {
state: IState = {
lots: null
}
calculate = () => {
this.setState({ lots: 1 });
}
render() {
<div>
<button className="btn btn-primary" onClick={this.calculate}>
Calculate
</button>
<div className="col-md-4">
<h5 className="card-title">Result</h5>
<label>Lots: {lots ? lots!.toFixed(3) : 'N/A'}</label>
</div>
</div>
}
}
Notice that we update lots.toFixed(3)
in Flow type to lots!.toFixed(3)
.
I’m a beginner TypeScript and surely I have a large room to improve. Hopefully, my very first experience with TypeScript can help someone out there who wanna give TypeScript a shot.