Table of Contents

  1. Introduction & Setup
  2. Compiling TypeScript
  3. Type Basics
  4. Objects & Arrays
  5. Explicit Types
  6. Dynamic (Any) Types
  7. Better Workflow & tsconfig
  8. Function Basics
  9. Type Aliases
  10. Function Types (Signatures)
  11. The DOM & Type Casting
  12. Classes
  13. Public, Private, Readonly
  14. Modules
  15. Interfaces
  16. Interfaces and Classes
  17. Rendering an HTML Template
  18. Generics
  19. Enums
  20. Tuples

  1. Introduction & Setup
  • superset of JavaScript with strict type system amongst other things
    • error checking and debugging becomes easier
  • because browsers don't understand TS by default, it must be compiled to JS
    npm init
    npm install --save-dev typescript
    npm install --save-dev commonjs
  • Additional features like generics, interfaces, tuples, and more

  1. Compiling TypeScript
  • Using Microsoft's Live Preview to have a server serving this content in the browser at port 3000
  • TypeScript code must be compiled before if can be rendered in the browser
  • This can be done with the following command:
npx tsc app.ts app.js

where app.ts could be replaced with any input TS filename, and app.js could be replaced with any output JS filename

  • can't have both files open at the same due to naming conflicts
  • if you want the input and output files to have the same name you can leave off the output file name
  • if you want typescript to watch a file and automatically recompile TS code to JS code on saved changes:
npx tsc app.ts -w

  1. Type Basics
  • the type a variable is at declaration is the type it must stay as
    • if you try to assign a different type to it later, you will get an error
    • you cna however change it's value withing the declaration type
  • TS infer types based on assignment, but they can also be explicitly defined
  • You should however define the type for function parameters to ensure type checking
  • Type checking is done at compilation > i.e. code won't compile to JS if type errors exist

  1. Objects and Arrays
  • All elements of an array must be of the same type if the original array is declared with just a single type
  • You can have an array of mixed types only if it is declared with mixed types
    • you can also redefine an index of the array as a different type i.e. if arr[0] was defined as a number, it can reassigned to any type as long as the array was declared with mixed types
  • In an object, the key-value pair assignents work like variable assignments in that the type of a particular key cannot be changes
  • Once an object is declared, you cannot add or remove a property
  • Additionally, arrays must remain arrays, and likewise for objects

  1. Explict Types
  • To initialize a variable without initializing it, we should give it an explicit type since there is nothing for TS to infer from.
let age: number;
  • The same type strictness will now apply
  • A similar thing can be done with arrays
let ninjas: string[] = [];
// assigning to an empty array so that it is alread initialized for use
  • We use the union type to define multiple the types a variable may house
let ninjas: (string | number)[] = [];
// could add a string or number, but not a boolean

let uuid: string | number;
  • We can similarly define a variable as an object, which is a superclass of array
let obj: object;
// could be an object or array

let obj2: {
  name: string;
  age: number;
};
// more structure to be enforced when the variable is given a value

  1. Dynamic (Any) Types
  • any type for variables that can be of any type, and can therefore also change type over time
let age: any;
age = 25;
age = "twenty-five";
// both are acceptable

let anyArr: any[] = [];
// can add any values to this array

let obj: {
  name: any;
  age: any;
};
// same for an object; still can't add or remove properties
  • kind of reverts TS back to JS, removing a lot of its useful features

  1. Better Workflow & tsconfig
  • May want to structure you project separating your public files (i.e. CSS, HTML, compiled JS code), from your src source TS files
  • But how do we connect these files? > with tsconfig
npx tsc --init
  • This creates a tsconfig.json file:
    • could change "target" to ESNext from es5, indicating the higest JS version your TS version supports
    • could also change "module" to ESNext
    • set "outDir" to where you want compiled JS files, and "rootDir" to where your source TS files will be housed
  • Must also tell TS what to watch now with npx tsc -w
    • unfortunately, this now has TS watching all file with the extension .ts, and compiling them in the public folder, even those outside of the source src folder
    • to address this, you can specify which files should be included in watching in the tsconfig.json file by adding the following at the top level of the object:
    {
      ...,
      "include": ["src"]
    }

  1. Function Basics
  • TS will infer the type of a function variable as well when it is defined
  • Could also define this explicitly
let greet: Function;
// can be a traditional or arrow function
  • Recall, you can also explicitly type function parameters
  • You can also define functions with optional types, and make parameters themselves optional as well, and default values
const add = (a: number, b: number, c: number | string) => {};
// optional type

const add = (a: number, b: number, c?: number | string) => {};
// optional variable; defaults to undefined

const add = (a: number, b: number = 10, c?: number | string) => {};
// default value; don't need to make a variable optional if it is given a default
// leave optional and defalut params until after other params
  • The return type of a function may be inferred, but best to be explicit
const add = (a: number, b: number = 10): void => {
  console.log("adding...");
};
// void return type

const add = (a: number, b: number = 10): number => {
  return a + b;
};
// numeric return type
  • If you assign the return value of a function to a variable, note that this will set the infered type of that variable, and it cannot be changed
let result = add(1, 3);
// the type of result is now inferred to be the return type of add() which is a number

  1. Type Aliases
  • Type aliases can be defined to reuse type specifications for DRYer code
type StringOrNum = string | number;
// type alias

const logDetails = (uuid: StringOrNum) => {};

const logMore = (uuid: StringOrNum, hash: number) => {};

  1. Function Types (Signatures)
  • Function signature decribe in more detail a function, including the arguments (nd their types) it takes, and the return value(s) and type(s)
    • names of params in signature need not match the final name of the params in the defined function
let logDetails: (obj: { name: string; age: number }) => void;

type person = { name: string; age: number };

logDetails = (ninja: person): void => {
  console.log(`${ninja.name} is ${ninja.age} years old`);
};

  1. The DOM & Type Casting
  • TypeScript doesn't have special access to your HTML, so it doesn't know if certain element will exist (or be null) after compilation
  • As a result, you must either handle for null values (conditional statements), or, explicitly let TS know that a particular HTML element exists
    • this is done by putting an exclaimation point at the end of assignment before the semi-colon
  • TS also has special types for each DOM element type
    • knows all properties and methods available on that element type
    • for example, it knows that <a> tags (of type HTMLAnchorElement) have a href property
  • TS however cannot infer the type of an element if it is grabbed by something other then it's tag type i.e. if you grab it by its class
    • in this case, you must type cast that element if you want access to its specific type properites and methods

  1. Classes
  • Classes in TS behave much like how they do in JS, with the addition of the strict type system
  • To make an array of a user-defined class, you can do the following:
class MyClass{
  ...
}

let myClassArr: myClass[] = [];

  1. Public, Private, Readonly
  • TypeScript provides access modifiers (like in Java) for class properties and methods
    • public: any access and modification inside and outside of the class
    • private: only access and modification within a class
    • readonly: can access from inside and outside the class, but not modify it in either context
  • accessors can be applied whenever a variable is first declared, either at the beginning of a class or within a class constructor

  1. Modules
  • We may wish to split our code out into separate files via ES6 modules i.e. what you import and export
    • because modules are only supported for newer browsers, TS doesn't compile them down to something older browsers will understand, so it's important to be using a modern browser to enable this functionality
    • Another option is webpack + TS (out of scope) for bundling and support for older browsers
  • To use modules, in any <script></script> tags that have a compiled JS module (i.e. one that was written in TS then compiled to JS code in youas a src, set the property type="module"

  1. Interfaces
  • for the difference between type aliases and intefaces (they look quite similar...) read this
  • recall, interfaces are like contracts, telling you what you can expect from a class that implements or other interface that extends it
  • an instantiation of an interface must have all properties and only the properties defined in the interface
  • this can be very helpful in enforcing what argements are passed as parameters to a function
// interfaces

interface IsPerson {
  name: string;
  age: number;
  speak(a: string): void;
  spend(a: number): number;
}

const me: IsPerson = {
  name: "reed",
  age: 25,
  speak(text: string): void {
    console.log(text);
  },
  spend(amount: number): number {
    console.log(` spent ${amount}`);
    return amount;
  },
};

  1. Interfaces and Classes
  • classes can implement interfaces
  • intefaces can be used much like types in that they can be used to enforce the structure and content of variable, lists, and classes

  1. Rendering an HTML Template
  • make use of the render() function defined in a class directly
  • interacting with the DOM directly using the document JS interface

  1. Generics
  • Generics are a feature in TS that allow us to create blocks of reusable code that can be used with different types
const addUID = (obj: object) => {
  let uid = Math.floor(Math.random() * 100);
  return { ...obj, uid };
};

let docOne = addUID({ name: "yoshi", age: 40 });
console.log(docOne);
console.log(docOne.name); // error

// but can't access any properties of the object because the function doesn't know what kind of object this is
// and therefore, we can't know the properties of the returned object
// must use generics
  • we can capture the specifics of whatever type is passed in like this
const addUID = <T>(obj: <T>) => {
  let uid = Math.floor(Math.random() * 100);
  return { ...obj, uid };
};

// but we're no longer enforcing that this parameter must be an objects
  • instead, we can enforce that whatever is returned must extend the type that is passed in
const addUID = <T extends object>(obj: <T>) => {
  let uid = Math.floor(Math.random() * 100);
  return { ...obj, uid };
};
  • or even more precisely
const addUID = <T extends {name: string}>(obj: <T>) => {
  let uid = Math.floor(Math.random() * 100);
  return { ...obj, uid };
};
  • alternatively, generics can be implemented with interfaces
interface Resource<T> {
  uid: number;
  resourceName: string;
  data: T;
}

const docThree: Resource<object> = {
  uid: 1,
  resourceName: "person",
  data: { name: "reed" },
};

const docFour: Resource<string[]> = {
  uid: 1,
  resourceName: "person",
  data: ["reed", "mcdaniel"],
};

  1. Enums
  • enums are a special data type that allow us to store a collection of constants and assiciate each one with a numeric value
enum ResourceType {
  BOOK,
  AUTHOR,
  FILM,
  DIRECTOR,
  PERSON,
}
interface Resource<T> {
  uid: number;
  resourceType: ResourceType;
  data: T;
}

const docThree: Resource<object> = {
  uid: 1,
  resourceType: ResourceType.BOOK,
  data: { name: "reed" },
};

console.log(docThree.resourceType); // returns 0 i.e. the index of BOOK in the enum
  1. Tuples
  • tuples, like in python, are immutable in that the type at a particular index cannot be changed
// lists
let arr = ["ryu", 25, true];
arrr[0] = false;
arr = [30, false, "yoshi"];
// the above is all fine for lists

//tuple
let tup: [string, number, boolean] = ["ryu", 25, true];
tup[0] = false; // error
tup[0] = "yoshi"; // allowed
  • tuples are useful for eforcing structure on a collection, for example passing in args as a tuple
let student: [string, number];
student = [98, "reed"]; // error
student = ["reed", 98]; //allowed