Introduction to TypeScript
Having learned the fundamentals of JavaScript (You haven't? Then please go and complete the JavaScript lessons first!), it's likely you are becoming familiar with the strengths and weaknesses of working with a dynamically typed language, vs a statically typed one (such as Java). One of the limitations of JavaScript is that it's dynamically typed, and that's that. Static typing isn't even an option. However, there are programming languages which give you the option of using dynamic or static types, as required. As you can probably imagine, there are all kinds of scenarios where that raw versatility comes in extremely handy.
TypeScript - JavaScript with (Optional) Static Typing
One language which gives you the choice of whether to use static or dynamic types is TypeScript. Actually, TypeScript is a superset of JavaScript, and converts to pure JavaScript before being run, so, having learned JS basics recently, you already know the fundamental syntax. All JavaScript code is valid TypeScript code. Amazing!
Static Type Syntax
The best and quickest way to understand what TypeScript is and how it works, is to compare a snippet of pure JavaScript code with it's TypeScript (TS) counterpart. Take a look at the following JS variable declarations:
const PI = 3.14159;
const meaningOfLife = 42;
const fullName = "John Doe";
const chocolateTastesGood = true;
As you already know, this is how all variables are declared in JS - no static types specified. Instead, the type of the variable is inferred through assignment. However, in TypeScript, we can assign static types to these variables via the following syntax:
const PI: number = 3.14159;
const meaningOfLife: number = 42;
const fullName: string = "John Doe";
const chocolateTastesGood: boolean = true;
What are the advantages of doing so? Having previously worked with a statically typed language (Java), you've already experienced the benefits - it's all about type safety. A lot of issues are caught at compile time that wouldn't be otherwise. Errors in your business logic relating to inappropriate use of mismatched types can be largely prevented. Essentially, static typing makes a large codebase easier to debug, maintain, document, and scale.
Other TS Goodies
As the name suggests, static typing is the most significant addition TypeScript makes to the JS language - but it's not the only addition. TS also adds an enum type, private, public and protected access modifiers for class members, generics, and a bunch of other cool bells and whistles that you'd typically expect to see in a general purpose, statically typed language. Here are some examples:
// let's use a few of the nifty TS features for the first time
// declare an enum
enum Department {
SALES,
TECH,
ACQUISITIONS
}
// class syntax is similar to ES6
class Employee {
// all members are declared at the top, before the constructor
private _name: string;
private _dob: Date;
private _department: Department;
constructor(name: string, dob: Date, department: Department) {
this._name = name;
this._dob = dob;
this._department = department;
}
// because we've declared our members as private above (best practice), we need to provide getters and setters.
// in TS, like in ES6, we can do this with the get and set keywords
public get name () {
return this._name;
}
public get dob () {
return this._dob;
}
public get department () {
return this._department;
}
// we'll provide a setter for department, in case an employee is redeployed internally
public set department (newDepartment: Department) {
this._department = newDepartment;
}
private privateMember = () => {
// this method can only be called from inside the class, TS will give compile errors otherwise
}
public publicMember = () => {
// this method is accessible outside the class
console.log('this is a public member');
}
protected protectedMember = () => {
// a protected member can be accessed within the class, or by sub-classes
}
}
const person1: Employee = new Employee('Richard', new Date('1987-07-16'), Department.TECH);
console.log(person1.department);
Running our Code: Transpilation
If you copy the above code snippet, sadly, it won't run - it's not valid JavaScript. In order to run TypeScript in the browser, it needs to be transpiled from TS to JS. We accomplish this with the typescript package. Before you work with TS, it's important to install typescript globally like so:
npm install -g typescript
Verify that the install was successful by calling the typescript compiler, via the tsc
command:
tsc --version
TypeScript Configuration: tsconfig.json
We're almost ready to stard transpiling our TS code to runnable JS code, but before we do, there is one more important step - setting up our tsconfig.json
file in the parent directory of our TS project. TypeScript will look for this tsconfig.json
file, because it tells it how to transpile to JS - for example, what module system to use, whether to transpile to ES5 or ES6, and so on. Here is an example of a barebones config file:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"sourceMap": true
}
}
As you can see above, tsconfig.json
is a simple JSON file. All properties belong to a parent object (hence the curley braces wrapping around everything at the top level). The most significant settings are housed within compilerOptions
. Here, we set our module
system to CommonJS
, and our JS target
to ES5
(supported by all browsers).
Time to Transpile!
Now it's time to transpile our code! To do so, just run tsc, and specify the name of the typescript file you intend to generate JS from:
tsc index.ts
Upon successful execution of the above command, you should see an index.js file appear in the same folder. If you open it up, you'll see that the file has been converted to pure JS. It should look something along the lines of the following:
"use strict";
// let's use a few of the nifthy TS features for the first time
// declare an enum
var Department;
(function (Department) {
Department[Department["SALES"] = 0] = "SALES";
Department[Department["TECH"] = 1] = "TECH";
Department[Department["ACQUISITIONS"] = 2] = "ACQUISITIONS";
})(Department || (Department = {}));
// class syntax is similar to ES6
class Employee {
constructor(name, dob, department) {
this.privateMember = () => {
// this method can only be called from inside the class, TS will give compile errors otherwise
};
this.publicMember = () => {
// this method is accessible outside the class
console.log('this is a public member');
};
this.protectedMember = () => {
// a protected member can be accessed within the class, or by sub-classes
};
this._name = name;
this._dob = dob;
this._department = department;
}
// because we've declared our members as private above (best practice), we need to provide getters and setters.
// in TS, like in ES6, we can do this with the get and set keywords
get name() {
return this._name;
}
get dob() {
return this._dob;
}
get department() {
return this._department;
}
// we'll provide a setter for department, in case an employee is redeployed internally
set department(newDepartment) {
this._department = newDepartment;
}
}
const person1 = new Employee('Richard', new Date('1987-07-16'), Department.TECH);
console.log(person1);
This file could be loaded into webpage and executed just like any other JS file.
Recap
In this general introduction to TypeScript, we learned what TypeScript is, what it adds to JavaScript, and the basic steps involved in transpiling it to useable JS. TS can be exciting, but please remember that ultimately, it gets converted to JS before execution, so it's still JS at the core, just with a bunch of really useful features added on top, which can greatly enhance the development experience on medium to large scale projects. A rule of thumb many developers use is to use pure JS for small projects, and TS for everything else. To learn more, visit the official microsoft documentation here.