This is my typescript playground.
I use the following environment:
% node --version
v9.11.1
% npm --version
5.6.0
First of all I need to setup the project:
npm init
Install the typescript compiler:
npm install --save-dev typescript
My typescript:
% ./node_modules/.bin/tsc --version
Version 2.8.1
Create new src directory to place all the typescript
files.
Ok, my goal is to create a simple command line app.
My expectation is like the following:
src/index.ts -> compile -> dist/index.js
Is it true? let's find out.
Btw how to write a main function in TypeScript?
The documentation about the function is available here.
hmmm TS is a superset of JS, so the way function declared is the same:
// Main function
function main() {
console.log("Hello");
}
main();Let's compile the src/index.ts to dist/index.js:
./node_modules/.bin/tsc src/index.ts --outDir dist
okay, use the following command to run the compiled code:
node dist/index.js
The next step is, how to get the process arguments?
We want to build the following CLI app:
hello NAME --age=12
it should accept required argument NAME and optional
argument --age.
We will use process
a global module that available in node.
// Main function
function main() {
console.log(process.argv);
}
main();Let's compile this code:
% ./node_modules/.bin/tsc src/index.ts --outDir dist
src/index.ts(3,17): error TS2304: Cannot find name 'process'.
ouh, error happen: Cannot find name 'process'.
Why? what happen?
Oh typescript need a type definition, there is a repository called DefinitelyTyped that contains a high quality TypeScript type definitions.
So I need to get the node type definition using the following command:
npm install --save-dev @types/node
Let's try to compile again:
./node_modules/.bin/tsc src/index.ts --outDir dist
nice, no error.
Let's run the compiled javascript:
% node dist/index.js
[ '/usr/local/Cellar/node/9.11.1/bin/node',
'/Users/pyk/pyk/typescript-playground/dist/index.js' ]
OK.
Btw the process in typescript is defined
here.
Ok, let's continue the CLI App.
We need to store the required argument in variable. The documentation about variable in typescript is available here.
TLDR:
- Use
letto represent a mutable variable. - Use
constto represent a immutable variable.
OK.
// Main function
function main() {
// If required argument is not supplied then
// exit the CLI
if (process.argv.length <= 2) {
console.error("Argument name is required");
process.exit(1);
}
// We assume the argument is supplied using the
// following format:
// $ hello name
// How to define a variable in typescript?
const name = process.argv[2];
console.log(`Hello ${name}`);
}
main();Compile the code:
./node_modules/.bin/tsc src/index.ts --outDir dist
Run the compiled javacript:
% node dist/index.js
Argument name is required
% node dist/index.js bayu
Hello bayu
Nice.
Currently we assume that our CLI app only is used only
when NAME is not supplied and NAME is supplied.
- What about the optional argument
--age? how to get the value? - What if there is a invalid optinal argument? such as
--dummyetc.
So we have the following scenarios:
# Required first usage
$ hello bayu --age 10
$ hello bayu --age=10
# Optional first usage
$ hello --age 10 bayu
$ hello --age=10 bayu
# Invalid options
$ hello bayu --dummy value
$ hello --dummy=value bayu
I think we can transform this scenario into unit test. Given an array of string, returns the parsed argument.
["bayu", "--age", 10] -> {"name": "bayu", "age": 10}
["--age", 10, "bayu"] -> {"name": "bayu", "age": 10}
How to write unit test in typescript?
hmmm.
Let's try using Jest first.
Setup Jest for our typescript project first. Install the required packages:
npm install --save-dev jest ts-jest @types/jest
Add the following config to package.json:
"jest": {
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
}
Create new file tsconfig.json with the following content:
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outDir": "./dist",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
tsconfig.json is a compiler options, so we can run tsc
without any argument and it will compile as configured:
./node_modules/.bin/tsc
tsconfig.json is also required by
ts-jest.
Ok. Let's try the jest first:
% ./node_modules/.bin/jest
No tests found
In /Users/pyk/pyk/typescript-playground
9 files checked.
testMatch: - 9 matches
testPathIgnorePatterns: /node_modules/ - 9 matches
testRegex: (/__tests__/.*|(\.|/)(test|spec))\.(jsx?|tsx?)$ - 0 matches
Pattern: - 0 matches
You will see that jest have not found a test yet. So, let's
create a new one.
Suppose we want to test the following function:
function hello(name: string): string {
return `Hello ${name}`;
}
export { hello };add the code above to src/cli.ts.
The next step is to write a test function for it.
Create new file src/cli.spec.ts with the following
content:
// Import module
import * as cli from "./cli";
test("hello('bay') should returns 'Hello bay'", () => {
expect(cli.hello("bay")).toBe("Hello bay");
})And run the jest:
% ./node_modules/.bin/jest
PASS src/cli.spec.ts
✓ hello('bay') should returns 'Hello bay' (3ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.316s
Ran all test suites.
NICE!!!
Btw we can add this scripts to package.json:
"scripts": {
"test": "./node_modules/.bin/jest",
"build": "./node_modules/.bin/tsc"
}
Usage:
npm run test
npm run build