TypeScript is a typed superset of Javascript that compiles to plain JavaScript
Any browser. Any host. Any OS. Open source
Making JavaScript scale
Easier to build and maintain medium to large applications
npm install -g typescript
tsc -v
Version 1.8.10
Lightweight ways to record the intended contract of the function or variable
let isEnabled: boolean = false;
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let name: string = "John";
name = 'John Smith';
let fullName: string = `Miguel Cobá`;
let age: number = 36;
let greeting: string = `Hello, my name is ${ fullName }.
I will be ${ age + 1 } years old next month.`
All the elements must be of the same type
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
Allow you to express an array where the type of a fixed number of elements is known, but need not be the same
// Declare a tuple type for a pair of string and number
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error
An enum is a way of giving more friendly names to sets of numeric values
// By default, enums begin numbering their members starting at 0
enum Color {Red, Green, Blue};
let c: Color = Color.Green;
// You can change this by manually setting the value of one of the members
enum Color {Red = 1, Green, Blue};
let c: Color = Color.Green;
// manually set them
enum Color {Red = 1, Green = 2, Blue = 4};
let c: Color = Color.Green;
Allows to opt-out of type-checking and let the values pass through compile-time checks
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
let list: any[] = [1, true, "free"];
list[1] = 100;
Opposite of any: the absence of having any type at all
function warnUser(): void {
alert("This is my warning message");
}
// Void variables are not useful because only undefined or null can be assigned to them:
let unusable: void = undefined;
When you know the type of some entity could be more specific than its current type
Is like a type cast in other languages
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
var declarations are accessible anywhere within their containing function, module, namespace, or global scope regardless of the containing block
Some people call this var-scoping or function-scoping
function f(shouldInitialize: boolean) {
if (shouldInitialize) {
var x = 10;
}
return x;
}
f(true); // returns '10'
f(false); // returns 'undefined'
Use lexical-scoping or block-scoping
Block-scoped variables are not visible outside of their nearest containing block or for-loop
function f(input: boolean) {
let a = 100;
if (input) {
// Still okay to reference 'a'
let b = a + 1;
return b;
}
// Error: 'b' doesn't exist here
return b;
}
They can’t be read or written to before they’re actually declared
a++; // illegal to use 'a' before it's declared;
let a;
Valid with var
// All the x refer to same variable in function scope
function f(x) {
var x;
var x;
if (true) {
var x;
}
}
Invalid with let
let x = 10;
let x = 20; // error: can't re-declare 'x' in the same scope
function f(x) {
let x = 100; // error: interferes with parameter declaration
}
function g() {
let x = 100;
var x = 100; // error: can't have both declarations of 'x'
}
Redeclaration is valid if in a different nested block
function f(condition, x) {
if (condition) {
let x = 100; // shadowing
return x;
}
return x;
}
f(false, 0); // returns '0'
f(true, 0); // returns '100'
They are like let declaration
Their value cannot be changed once they are bound
const numLivesForCat = 9;
Doesn't mean that the values they refer to are immutable
const numLivesForCat = 9;
const kitty = {
name: "Aurora",
numLives: numLivesForCat,
}
// Error
kitty = {
name: "Danielle",
numLives: numLivesForCat
};
// all "okay"
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;
Principle of least privilege: all declarations other than those you plan to modify should use const
The destructuring assignment syntax is an expression that makes it possible to extract data from arrays or objects into distinct variables
let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2
// swap variables
[first, second] = [second, first];
function f([first, second]: [number, number]) {
console.log(first);
console.log(second);
}
f(input);
You can create a variable for the remaining items in a list using the syntax ...name
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]
You can ignore parameters
let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1
let [, second, , fourth] = [1, 2, 3, 4];
let o = {
a: "foo",
b: 12,
c: "bar"
}
let {a, b} = o; // This creates new variables a and b from o.a and o.b.
You can also give different names to properties
let {a: newName1, b: newName2} = o;
// is equivalent to
let newName1 = o.a;
let newName2 = o.b;
The type of the object needs to be written after the destructuring
let {a, b: newName}: {a: string, b: number} = o;
Default values let you specify a default value in case a property is undefined
function keepWholeObject(wholeObject: {a: string, b?: number}) {
let {a, b = 1001} = wholeObject; // b will be 1001 if wholeObject.b is undefined
}
TypeScript's type-checking focuses on the shape that values have
This is sometimes called "duck typing" or "structural subtyping"
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
// myObj is checked based on its shape
// i.e. it has the same structure that function signature (label property)
printLabel(myObj);
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj); // no need for myObj to implement the interface, only its shape matters
Interfaces with optional properties are written similar to other interfaces, with each optional property denoted by a ? at the end of the property name in the declaration
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
Interfaces are also capable of describing function types
interface SearchFunc {
(source: string, subString: string): boolean;
}
Names of the parameters do not need to match
let mySearch: SearchFunc;
mySearch = function(src, sub) {
let result = src.search(sub);
if (result == -1) {
return false;
}
else {
return true;
}
}
Names of the parameters do not need to match
let mySearch: SearchFunc;
mySearch = function(src, sub) {
let result = src.search(sub);
if (result == -1) {
return false;
}
else {
return true;
}
}
Allows to explicitly enforce that a class meet a particular contract
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
An interface can extend multiple interfaces, creating a combination of all of the interfaces.
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
outputs
Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.
Members are public by default
When a member is marked private, it cannot be accessed from outside of its containing class
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // Error: 'name' is private;
Like the private modifier but protected members can also be accessed by instances of deriving classes
class Person {
protected name: string;
constructor(name: string) { this.name = name; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // error
Let you create and initialize a member in one place
Are declared by prefixing a constructor parameter with an accessibility modifier
class Animal {
constructor(private name: string) { }
move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
Intercepting accesses to a member of an object
class Employee {
fullName: string;
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
console.log(employee.fullName);
}
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
console.log("fullName changed");
this._fullName = newName;
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
console.log(employee.fullName);
}
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
Base classes from which other classes may be derived
They may not be instantiated directly
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("roaming the earth...");
}
}
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log("Department name: " + this.name);
}
abstract printMeeting(): void; // must be implemented in derived classes
}
class AccountingDepartment extends Department {
constructor() {
super("Accounting and Auditing"); // constructors in derived classes must call super()
}
printMeeting(): void {
console.log("The Accounting Department meets each Monday at 10am.");
}
generateReports(): void {
console.log("Generating accounting reports...");
}
}
let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: method doesn't exist on declared abstract type
function add(x, y) {
return x + y;
}
let myAdd = function(x, y) { return x+y; };
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x+y; };
It has two parts:
Uses => to separate parameters and return type
let myAdd: (x: number, y: number)=>number =
function(x: number, y: number): number { return x + y; };
// myAdd has the full function type
let myAdd = function(x: number, y: number): number { return x + y; };
// The parameters 'x' and 'y' have the type number
let myAdd: (baseValue:number, increment:number) => number =
function(x, y) { return x + y; };
In TypeScript, every parameter is required
The number of arguments given to a function has to match the number of parameters the function expects
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams");
In JavaScript, every parameter is optional
We can get this functionality in TypeScript by adding a ? to the end of parameters
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // works correctly now
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result4 = buildName("Bob", "Adams"); // ah, just right
Boundless number of optional parameters
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
A union type describes a value that can be one of several types
We use the vertical bar (|) to separate each type
/**
* Takes a string and adds "padding" to the left.
* If 'padding' is a string, then 'padding' is appended to the left side.
* If 'padding' is a number, then that number of spaces is added to the left side.
*/
function padLeft(value: string, padding: string | number) {
// ...
}
let indentedString = padLeft("Hello world", true); // errors during compilation
We can only access members that are common to all types in the union
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim(); // errors
Type aliases create a new name for a type
Aliasing doesn’t actually create a new type - it creates a new name to refer to that type
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === "string") {
return n;
}
else {
return n();
}
}
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(dx: number, dy: number, easing: Easing) {
// ...
}
}
let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
A polymorphic this type represents a type that is the subtype of the containing class or interface
class BasicCalculator {
public constructor(protected value: number = 0) { }
public currentValue(): number {
return this.value;
}
public add(operand: number): this {
this.value += operand;
return this;
}
public multiply(operand: number): this {
this.value *= operand;
return this;
}
}
let v = new BasicCalculator(2).multiply(5).add(1).currentValue();
class ScientificCalculator extends BasicCalculator {
public constructor(value = 0) {
super(value);
}
public sin() {
this.value = Math.sin(this.value);
return this;
}
}
let v = new ScientificCalculator(2).multiply(5).sin().add(1).currentValue();
Smbol is a primitive data type
Symbol values are created by calling the Symbol constructor
let sym1 = Symbol();
let sym2 = Symbol("key"); // optional string key
Symbols are immutable and unique
let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 === sym3; // false, symbols are unique
Symbols can be used as keys for object properties
let sym = Symbol();
let obj = {
[sym]: "value"
};
console.log(obj[sym]); // "value"
// also for function members
const getClassNameSymbol = Symbol();
class C {
[getClassNameSymbol](){
return "C";
}
}
let c = new C();
let className = c[getClassNameSymbol](); // "C"
Lops over an iterable object, invoking the Symbol.iterator property on the object
let someArray = [1, "string", false];
for (let entry of someArray) {
console.log(entry); // 1, "string", false
}
for..in returns a list of keys on the object being iterated
for..of returns a list of values of the numeric properties of the object being iterated.
let list = [4, 5, 6];
for (let i in list) {
console.log(i); // "0", "1", "2",
}
for (let i of list) {
console.log(i); // "4", "5", "6"
}
for..in operates on any object; it serves as a way to inspect its properties
for..of is mainly interested in values of iterable objects
let pets = new Set(["Cat", "Dog", "Hamster"]);
pets["species"] = "mammals";
for (let pet in pets) {
console.log(pet); // "species"
}
for (let pet of pets) {
console.log(pet); // "Cat", "Dog", "Hamster"
}
Any declaration (variable, function, class, type alias, or interface) can be exported
Use export keyword.
export let color = 'red';
export const PI = 3.1415;
// StringValidator.ts
export interface StringValidator {
isAcceptable(s: string): boolean;
}
// ZipCodeValidator.ts
export const numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
// ZipCodeValidator.ts
class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };
export class ParseIntBasedZipCodeValidator {
isAcceptable(s: string) {
return s.length === 5 && parseInt(s).toString() === s;
}
}
// Export original validator but rename it
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";
import { ZipCodeValidator } from "./ZipCodeValidator";
let myValidator = new ZipCodeValidator();
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
let myValidator = new ZCV();
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();
// ZipCodeValidator.ts
export default class ZipCodeValidator {
static numberRegexp = /^[0-9]+$/;
isAcceptable(s: string) {
return s.length === 5 && ZipCodeValidator.numberRegexp.test(s);
}
}
// test.ts
import validator from "./ZipCodeValidator";
let myValidator = new validator();
// StaticZipCodeValidator.ts
const numberRegexp = /^[0-9]+$/;
export default function (s: string) {
return s.length === 5 && numberRegexp.test(s);
}
// test.ts
import validate from "./StaticZipCodeValidator";
let strings = ["Hello", "98052", "101"];
// Use function validate
strings.forEach(s => {
console.log(`"${s}" ${validate(s) ? " matches" : " does not match"}`);
});
// OneTwoThree.ts
export default "123";
// Log.ts
import num from "./OneTwoThree";
console.log(num); // "123"
Write a function that receives:
Write a module that exports a Traveler class and TravelerType enum
Write a module that
https://miguelcoba.github.io/typescript-intro
Miguel Cobá