Common
We will begin with the simplest piece, common (or shared) code. This will also serve as a good introduction to TypeScript.
Initialize
We will begin by creating the structure and installing the necessary dependencies (make sure you have Yarn installed). Open up your console in the directory you want to build your application in and run the following commands:
- Initialize the Yarn-project and answer the prompts according to your application:
yarn init
- Add the necessary dependencies, TypeScript and TSLint (for code quality checks, a.k.a. linting). When you add dependencies in Yarn they will be saved to your
package.json
and Yarn will create ayarn.lock
file to manage the versions of those dependencies. The flag-D
saves them asdevDependencies
which will not be utilized when running the system in production.yarn add -D typescript tslint
- Open your project in an editor like Visual Studio Code or Atom, though I recommend Visual Studio Code as it has IntelliSense.
- Create the file
Todo.ts
inside a folder calledcommon
which will in turn be insidesrc
that is located at the root of your application.
Configuring TypeScript
Next we will configure TypeScript by creating a file in the root folder called tsconfig.json. tsconfig.json
indicates to TypeScript that the folder is a TypeScript project. Start by writing the following content into your tsconfig.json
:
{
"compilerOptions": {
"noImplicitAny": true,
"target": "es5",
"jsx": "react",
"moduleResolution": "node",
"lib": ["dom", "es2016", "es2017"],
"sourceMap": true
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
]
}
On the first row
"compilerOptions": {
we start by defining the compilerOptions.
The first rule we set
"noImplicitAny": true,
is noImplicitAny
which will enforce the use of type declarations (more about those later) when the type would otherwise be inferred as any
.
The second rule
"target": "es5",
defines the target ECMAScript version (in this case ES5) for the compiled JavaScript files.
In the third line
"moduleResolution": "node"
we set the moduleResolution mode for the TypeScript compiler as node
, which allows the importing of dependencies from the folder node_modules
(where Yarn saves them by default) by using non-relative imports.
On the fourth line
"lib": ["dom", "es2016", "es2017"]
we add support for DOM APIs like Window
and the newest features from es2016 and es2017.
On the fifth line
"sourceMap": true
we tell the compiler to generate source maps.
On the seventh line
"include": [
we set the files TypeScript will compile.
In this case we use a glob pattern) to indicate the TypeScript files on the eight and ninth lines.
"src/**/*.ts",
"src/**/*.tsx"
These globs will match all files in the src
folder (recursively) with the extension .ts
or .tsx
.
Start coding
We will begin by creating a class that defines our Todo-items. A Todo should have an id (to differentiate between todos), a title (tha actual todo) and information on whether the todo has been completed or not. Here we see the benefit of using TypeScript, as it allows us to define the actual information contained in a Todo (in plain JavaScript and all other dynamically typed languages you as a developer have to keep track of such things). I'll first show you the class in its simplest form and then explain what each keyword means.
export default class Todo {
readonly id: number;
readonly title: string;
readonly done: boolean;
}
On the first line
export default class Todo {
we first define that this class
is the default export (export default
) of our module, which means that in other files we can write
import Todo from '../path/to/Todo.ts'
instead of (just writing export
)
import { Todo } from '../path/to/Todo.ts'
to get a hold of our new Todo
-class.
Remember that a module can only have one default export.
After the export-clause we define the class Todo
. In TypeScript a class is something you can instantiate (create an instance of), and that can inherit other classes (have their properties as well).
On the second line
readonly id: number;
we define our first property for the class Todo
. The first word readonly defines the property as something that is immutable and from now on TypeScript a class
will give you an error if you try to change the value of a Todo
's number
-field. The second word id
is the name of the property (so later on we can access it by calling myInstanceOfTodo.id
). The last word here is the type of the property, meaning what kind of information the property can hold, in this case a number
(this particular type doesn't care if it is a whole number or a floating one).
The two following lines
readonly title: string;
readonly done: boolean;
are otherwise the same as the first one, except the property title
has a type of string
, meaning it's an arbitrary sequence of letters and characters and the property done
has a type of boolean
, meaning that its value is either true
or false
.
Congratulations, you have now created your very first TypeScript class
!
Linting
It's time to start linting your code by using TSLint. Let's begin by creating a Yarn script to run TSLint:
// In package.json
"scripts": {
"lint:ts": "tslint --type-check --project tsconfig.json"
}
The lint command can now be run with yarn run lint:ts
. This will now run TSLint with its default settings (using tsconfig.json's settings). However, that might not always be enough for you and if you want to define the rules for your own codebase more accurately, you can create a tslint.json
in the root folder and populate it with rules according to TSLint rules. For example in the boilerplate the tslint.json
looks like that:
{
"rules": {
"no-any": false, // Allows use of any as a type declaration
"no-magic-numbers": true, // Disallow magic numbers
"only-arrow-functions": [true], // Enforces the use of arrow functions instead of the traditional syntax
"curly": true, // Enforces curly braces in all ifs/fors/dos/whiles
"no-var-keyword": true, // Enforces the use of the new keywords let and const instead of the old var
"triple-equals": true, // Enforces the use of triple equals instead of double equals in conditionals
"indent": ["spaces"], // Enforces indentation using spaces instead of tabs
"prefer-const": true, // Enforces the use of const unless let is needed
"semicolon": [true, "always"], // Enforces that all lines should end in a semicolon
"eofline": true, // Enforces an empty line at the end of file
"trailing-comma": [true, { "multiline": "always", "singleline": "never" }], // Enforces a comma at the end of all parameters that end in a new line
"arrow-return-shorthand": [true], // Suggests one to use shorthand arrow functions when possible
"class-name": true, // Enforces PascalCased class names
"interface-name": [true, "always-prefix"], // Enforces all interfaces to follow PascalCasing and be prefixed with I
"quotemark": [true, "single"], // Enforces the use of single quotation marks
"no-unused-variable": [true, { "ignore-pattern": "^I"}], // Warns about unused variables, unless they start with a capital I (interfaces)
"no-unused-expression": [true, "allow-fast-null-checks"] // Warns about unused expressions, unless it is a null check e.g. someVariable && someVariable.doSomething()
}
}
Alternatives
Below you can find alternatives to TypeScript, if you don't fancy it as much as I do: