Typescript

Typescript is a typed superset of JavaScript that compiles to plain JavaScript.

  • Any browser.
  • Any host.
  • Any OS.
  • Open source.
  • Developed by Microsoft in 2012
  • Advance version of JavaScript

Install

npm install -g typescript

Compile

tsc helloworld.ts

Basic Syntax and Types

TypeScript is a typed language. This means that you can specify the type of a variable when you declare it, and only variables of that type can be assigned to that variable.

let a: number = 42;
// let variableName: type = value;

Core Types

There are three main primitives in JavaScript and TypeScript.

  • boolean - true or false values
let isDone: boolean = false;
  • number - whole numbers and floating point values
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
  • string - text values like "TypeScript Rocks"
let color: string = "blue";
color = 'red';

There are also 2 less common primitives used in later versions of Javascript and TypeScript.

  • bigint - whole numbers and floating point values, but allows larger negative and positive numbers than the number type.
let big: bigint = 100n;
  • symbol are used to create a globally unique identifier.

Type Inference

Type inference is a way to predict the type of a variable based on its value.

let a = 42; // a is a number

When creating a variable, there are two main ways TypeScript assigns a type:

  • Explicit
  • Implicit Explicit - writing out the type:
let firstName: string = "Dylan";

Implicit - TypeScript will guess the type:

let firstName = "Dylan";

Error in Type Assignment You can't re-declare a variable with a different type b/c its already delcare

let a: number = 42; // a is a number
a = "hello"; // error TS2322: Type '"hello"' is not assignable to type 'number'.

TypeScript Special Types

  • any - TypeScript will not check the type of this variable
let a: any = 42;
a = "hello";
  • unknown - similar to any, but you cannot access any properties of an unknown type, nor can you call/construct them.
let a: unknown = 30;
let b = a === 123;
let c = a + 10;
  • never - represents the type of values that never occur. For example, never is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns.
function error(message: string): never {
  throw new Error(message);
}
  • void - the absence of having any type at all. For example, declaring variables of type void is not useful because you can only assign undefined or null to them.
function warnUser(): void {
  console.log("This is my warning message");
}
  • null - represents an intentional absence of an object value.
let a: null = null;
  • undefined - is a variable that has been declared, but no value has been assigned to it.
let a: undefined = undefined;
  • object - represents any non-primitive type.
let a: object = {
  b: "x"
};
  • never - represents the type of values that never occur. For example, never is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns.
function error(message: string): never {
  throw new Error(message);
}

Arrays

  • Array - represents a list of elements of a single type.
let list: number[] = [1, 2, 3];

Tuples

  • Tuple - represents a list of elements of a single type, where the type of each element is known. You can think of a tuple as an array with a fixed number of elements.
let x: [string, number];
x = ["hello", 10]; // OK
x = [10, "hello"]; // Error
  • Literal Types - allow you to specify the exact value a string, number, or boolean must have.
let a: "hello" = "hello";
a = "howdy"; // Error!
  • Non-Null Assertion Operator - is an exclamation point (!) that tells TypeScript to temporarily suspend strict null checks.
function liveDangerously(x?: number | null) {
  // No error
  console.log(x!.toFixed());
}

Interfaces

One of TypeScript’s core principles is that type-checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural subtyping”. In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

interface Person {
  firstName: string;
  lastName: string;
}
function greeter(person: Person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}
let user = { firstName: "Jane", lastName: "User" };
document.body.innerHTML = greeter(user);

Union Types & Intersection Types & Function Types

  • Union - allows us to use more than one type for a value.
  • or (|) checking if one of condition is matched
let a: number | boolean = 35;
a = true;
  • Intersection - allows us to combine multiple types into one.
  • and (&) checking if both condition is matched
interface Order {
  id: string;
  amount: number;
  currency: string;
}
interface Stripe {
  card: string;
  cvc: string;
}
type CheckoutCard = Order & Stripe;
let order: CheckoutCard = {
  id: "xj28s",
  amount: 100,
  currency: "USD",
  card: "1000 2000 3000 4000",
  cvc: "123"
};
  • Function - is a special type that represents a JavaScript function.
  • the : number here specifies that this function returns a number
// the `: number` here specifies that this function returns a number
function getTime(): number {
  return new Date().getTime();
}
  • the : void here specifies that this function doesn't return anything
// the `: void` here specifies that this function doesn't return anything
function printHello(): void {
  console.log('Hello!');
}
  • the : never here specifies that this function never returns anything
// the `: never` here specifies that this function never returns anything
function throwError(errorMsg: string): never {
  throw new Error(errorMsg);
}
  • the : unknown here specifies that this function returns an unknown type
// the `: unknown` here specifies that this function returns an unknown type
function getID(id: number): unknown {
  return id;
}
  • the : any here specifies that this function returns any type
// the `: any` here specifies that this function returns any type
function getID(id: number): any {
  return id;
}
  • the parameter: type here specifies that this function takes in a parameter of a certain type
// the ` parameter: type` here specifies that this function takes in a parameter of a certain type
function addNumbers(a: number, b: number): number {
  return a + b;
}
  • the parameter?: type here specifies that this function takes in an optional parameter of a certain type
// the ` parameter?: type` here specifies that this function takes in an optional parameter of a certain type
function addNumbers(a: number, b?: number): number {
  return a + b;
}
  • the parameter: type = value here specifies that this function takes in a parameter of a certain type with a default value
// the ` parameter: type = value` here specifies that this function takes in a parameter of a certain type with a default value
function addNumbers(a: number, b: number = 10): number {
  return a + b;
}
  • the parameter: type[] here specifies that this function takes in a parameter of a certain type as an array
// the ` parameter: type[]` here specifies that this function takes in a parameter of a certain type as an array
function addNumbers(a: number, b: number[]): number {
  return a + b[0];
}
  • the parameter: ...type[] here specifies that this function takes in a parameter of a certain type as an array
// the ` parameter: ...type[]` here specifies that this function takes in a parameter of a certain type as an array
function addNumbers(a: number, ...b: number[]): number {
  return a + b[0];
}
  • the parameter: (parameter: type) => type here specifies that this function takes in a parameter of a certain type as an array
// the ` parameter: (parameter: type) => type` here specifies that this function takes in a parameter of a certain type as an array
function addNumbers(a: number, b: (c: number) => number): number {
  return a + b(10);
}

Optional & Default Parameters

  • Optional - parameters are parameters that don’t have to be included when the function is called.
function printIngredient(quantity: string, ingredient: string, extra?: string) {
  console.log(`${quantity} ${ingredient} ${extra ? ` ${extra}` : ""}`);
}
printIngredient("1C", "Flour");
printIngredient("1C", "Sugar", "something else");
  • Default - parameters are parameters that take a default value if no argument is passed to the function for that parameter.
function printIngredient(quantity: string, ingredient: string, extra: string = "") {
  console.log(`${quantity} ${ingredient} ${extra}`);
}
printIngredient("1C", "Flour");
printIngredient("1C", "Sugar", "something else");

Rest Parameters

  • Rest - parameters are similar to optional parameters, but they are declared with a prefix of three periods (...). This tells the compiler to create an array of parameters of that type from all the remaining parameters in the function.
function addNumbers(...nums: number[]) {
  return nums.reduce((total, num) => total + num, 0);
}
addNumbers(1, 2, 3, 4, 5); // returns 15
function pow(x: number, y: number): number {
  return Math.pow(x, y);
}
pow(5, 10); // returns 9765625
  • Function Signature - is a way to define a function where we only define the function type, but not the implementation.
let myFunc: (a: number, b: number) => number;
myFunc = (a: number, b: number) => {
  return a + b;
};
  • Function Overloading - is a way to define multiple function signatures for the same function name. This is how we can create functions that are more flexible.
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}

Type Aliases

Type aliases create a new name for a type. Type aliases are sometimes similar to interfaces, but can name primitives, unions, tuples, and any other types that you’d otherwise have to write by hand.

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 Aliases - allow us to create a new name for a type. Type aliases are sometimes similar to interfaces, but can name primitives, unions, tuples, and any other types that you’d otherwise have to write by hand.
type Age = number;
type Person = {
  name: string;
  age: Age;
};
const age: Age = 55;
const driver: Person = {
  name: "James May",
  age: age
};

Classes

class Student {
  fullName: string;
  constructor(public firstName: string, public middleInitial: string, public lastName: string) {
    this.fullName = firstName + " " + middleInitial + " " + lastName;
  }
}
interface Person {
  firstName: string;
  lastName: string;
}
function greeter(person: Person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}

Generics

Generics is a way to create reusable components. Generics provide variables to types. A common example is an array. An array without generics could contain anything. An array with generics can describe the values that the array contains.

function identity<T>(arg: T): T {
  return arg;
}

Enums

Enums allow us to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums.

enum Color {Red, Green, Blue}
let c: Color = Color.Green;

Type Annotations

Type annotations in TypeScript are lightweight ways to record the intended contract of the function or variable. In this case, we intend the greeter function to be called with a single string parameter. We can try changing the call greeter to pass an array instead:

function greeter(person: string) {
  return "Hello, " + person;
}
let user = [0, 1, 2];
document.body.innerHTML = greeter(user);

Types by Inference

TypeScript knows the JavaScript language and will generate types for you. For example, if you write the number 42, TypeScript will understand that you want a number and will generate the type number for you.

let a = 42; // a is a number

you can't re-declare a variable with a different type b/c its already delcare

let a = 42; // a is a number
a = "hello"; // error TS2322: Type '"hello"' is not assignable to type 'number'.

Defining Types of Object

To create an object with an inferred type which includes name: string and id: number, you can write:

let obj = {
  name: "John",
  id: 1
};

You can also explicitly declare the type of the object:

let obj: {
  name: string;
  id: number;
} = {
  name: "John",
  id: 1
};

Type Assertion

Type assertions are a way to tell the compiler “trust me, I know what I’m doing.”

let a: any = "this is a string";
let b = (<string>a).toUpperCase();

Modules

Modules are executed within their own scope, not in the global scope; this means that variables, functions, classes, etc. declared in a module are not visible outside the module unless they are explicitly exported using one of the export forms. Conversely, to consume a variable, function, class, interface, etc. exported from a different module, it has to be imported using one of the import forms.

export interface StringValidator {
  isAcceptable(s: string): boolean;
}

Namespaces

Namespaces are simply named JavaScript objects in the global namespace. This makes namespaces a very simple construct to use. They can span multiple files, and can be concatenated using --outFile. Namespaces can be a good way to structure your code in a Web Application, with all dependencies included as <script> tags in your HTML page.

namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }
}

Declaration Merging

TypeScript has a feature called declaration merging. This feature allows you to write multiple declarations and have them all appear as a single declaration.

interface Box {
  height: number;
  width: number;
}
interface Box {
  scale: number;
}
let box: Box = {height: 5, width: 6, scale: 10};

Triple-Slash Directives

Triple-slash directives are single-line comments containing a single XML tag. The contents of the comment are used as compiler directives.

/// <reference path="..." />

Type Checking JavaScript Files

TypeScript is a structural type system. When we compare two different types, regardless of where they came from, if the types of all members are compatible, then we say the types themselves are compatible.

interface Point {
  x: number;
  y: number;
}
function logPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}
const point = { x: 12, y: 26 };
logPoint(point);

Mixins

A mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes.

class Disposable {
  isDisposed: boolean;
  dispose() {
    this.isDisposed = true;
  }
}
class Activatable {
  isActive: boolean;
  activate() {
    this.isActive = true;
  }
  deactivate() {
    this.isActive = false;
  }
}
class SmartObject implements Disposable, Activatable {
  constructor() {
    setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
  }
  interact() {
    this.activate();
  }
  // Disposable
  isDisposed: boolean = false;
  dispose: () => void;
  // Activatable
  isActive: boolean = false;
  activate: () => void;
  deactivate: () => void;
}
applyMixins(SmartObject, [Disposable, Activatable]);
let smartObj = new SmartObject();
setTimeout(() => smartObj.interact(), 1000);

JSX

JSX is a syntax extension to JavaScript. It is similar to a template language, but it has full power of JavaScript. JSX gets compiled to React.createElement() calls which return plain JavaScript objects called “React elements”. You can also use JSX inside of TypeScript.

interface Props {
  name: string;
  X: number;
  Y: number;
}
declare function AnotherComponent(props: Props): JSX.Element;

React

React is a JavaScript library for building user interfaces. It is the view layer for web applications.

import * as React from "react";
import * as ReactDOM from "react-dom";
interface HelloProps {
  compiler: string;
  framework: string;
}
class Hello extends React.Component<HelloProps, {}> {
  render() {
    return (
      <h1>
        Hello from {this.props.compiler} and {this.props.framework}!
      </h1>
    );
  }
}
ReactDOM.render(
  <Hello compiler="TypeScript" framework="React" />,
  document.getElementById("example")
);

Documentation Links