-
Notifications
You must be signed in to change notification settings - Fork 3
typescript overview
Typescript is, essentially, Javascript plus type annotations. These types are checked at compile time, when Typescript code is compiled down to vanilla Javascript. This Javascript is then executed in Node, the browser, etc.
In addition to adding static type checking, Typescript uses a different syntax for modules, extends the syntax for declaring classes, and adds a few other minor tweaks.
Type annotations tell the compiler the types of your variables. They look like this:
let activeJob: Job = new Job();
and like this:
function fancify(value: number): string { // Takes a number, returns a string
return '#' + value + '#';
}
and like this:
class Job {
private _id: number;
private _name: string;
}
Type annotations are usually not required for the return types of functions or for variables that are initialized with a value. The compiler will infer the appropriate type:
let activeJob = new Job(); // activeJob has type `Job`
function negate(value: number) {
return -value; // negate() has inferred return type `number`
}
class Job {
private _id: number;
private _name = 'Default job name'; // _name has inferred type `string`
}
Typescript uses a different module system (ES6 modules) than the one that Node uses (require()
, module.exports
, etc.).
Instead of setting module.exports
, use the export
keyword in front of any declaration you want to be accessible by other modules:
export const BOUNCE_DURATION = 250;
export function bounceBall(ball: Ball) {
// ...
}
export class Ball {
// ...
}
There's special syntax for the "default" export (export default <foo>
), which is intended for modules that only export a single thing, but it's use is not recommended. Just use export
, even if you're only exporting one thing.
There are 4 different syntaxes for importing from other modules:
// Importing from another Typescript module (two options)
import { bounceBall, Ball } from './ball';
import * as ball from './ball';
// Import an NPM module that has a typings definition
import express = require('express');
// Import an NPM module that doesn't have a typings definition
const uncheckedModule = require('uncheckedModule');
In order to use NPM modules in Typescript, you need to install the typing definitions for that module. It tells the compiler what the module's public interface is. Look up the module you want to use in TypeSearch and then npm install
the path it gives you. Note that these type definitions are usually written by third parties and so can be fallible.
If a typing doesn't exist for your NPM module, you'll need to use Node-style imports:
const myModule = require('myModule');
Unfortunately, this means that any code that interacts with this module won't benefit from type-checking.
Variables have to be explicitly marked as nullable or undefinable if it's possible for them to hold one of those values:
let count: number = 3;
count = null; // Error: count doesn't have the 'null' type
count = undefined; // Error: count doesn't have the 'undefined' type
let nullableCount: number | null;
nullableCount = 3;
nullableCount = null; // Okay
If you try to access a possibly null value, the compiler will yell at you unless you wrap it in a null check:
function printNum(value: number | null) {
console.log(value.toString()); // ERROR: value might be null
if (value != null) {
console.log(value.toString()); // Okay
}
}
In Typescript, null
and undefined
have different, incompatible types. This incompatibility can be annoying at times, such as when you have an undefinable value but the function you want to use only takes a nullable value.
There are a few options for dealing with this complexity:
- Within a file (or group of dependent files) only use either
null
orundefined
, but never a mixture of both. Our current code tries to do this (it usesundefined
). This can be tricky, though! Tnex usesnull
for empty columns. At the same time, the most convenient way to specify optional parameters to functions usesundefined
. - Use the
nil
type instead, which is defined inutil/simpleTypes.ts
. This is an alias fornull | undefined
. If you use it everywhere, things will generally work out.
Optional parameters are generally discouraged, as they complicate the null
vs. undefined
issue above. If you must use them, you've got two options:
When you mark a function parameter or class member as "optional" via the ?
operator, it gains the undefined
type:
function doThing(foo?: number) { // foo's type is number | undefined
// ...
}
You can achieve the same result with null
using a default value, but it's a more verbose option:
function doThing(foo: number | null = null) {
// ...
}