Skip to content

Instantly share code, notes, and snippets.

@manikandanraji
Created December 29, 2022 15:55
Show Gist options
  • Save manikandanraji/6e0bd72a331db394d1bbe08c5b736cf7 to your computer and use it in GitHub Desktop.
Save manikandanraji/6e0bd72a331db394d1bbe08c5b736cf7 to your computer and use it in GitHub Desktop.
Notes from reading the book "Learning TypeScript"

Functions

Function Parameters

  • Without explicit type information declared, TypeScript will consider the function parameters to be the any type, meaning the parameter’s type could be anything.
  • TypeScript allows you to declare the type of function parameters with a type annotation.

Required Parameters

  • All parameters declared on a function are required.
  • If a function is called with a wrong number of arguments, TypeScript will protest in the form of a type error.

Optional Parameters

  • TypeScript allows annotating a parameter as optional by adding a ? before the : in its type annotation.
  • Their types therefore always have | undefined added as a union type.
  • Parameters that aren’t marked as optional with a ? must always be provided, even if the value is explicitly undefined.
  • Any optional parameters for a function must be the last parameters. Placing an optional parameter before a required parameter would trigger a TypeScript syntax error.

Default Parameters

  • Optional parameters in JavaScript may be given a default value with an = and a value in their declaration.
  • TypeScript type does not implicitly have the | undefined union added on inside the function.
  • TypeScript will still allow the function to be called with missing or undefined arguments for those parameters.

Rest Parameters

  • Some functions in JavaScript are made to be called with any number of arguments.
  • The … spread operator may be placed on the last parameter in a function declaration to indicate any “rest” arguments passed to the function starting at that parameter should all be stored in a single array.
function singAllTheSongs(singer: string, ...songs: string[]) {
  for (const song of songs) {
    console.log(`${song}, by ${singer}`)
  }
}

Explicity Return Types

  • Function declaration return type annotations are placed after the ) following the list of parameters.
  • For arrow functions (also known as lambdas), that falls just before the =>.
  • If a return

statement in a function returns a value not assignable to the function’s return type, TypeScript will give an assignability complaint.

Function Types

  • JavaScript allows us to pass functions around as values. That means we need a way to declare the type of a parameter or variable meant to hold a function.
  • Function type syntax looks similar to an arrow function, but with a type instead of the body.
let nothingInGivesString: () => string;

Void Returns

  • Some functions aren’t meant to return any value. They either have no return statements or only have return statements that don’t return a value. TypeScript allows using a void keyword to refer to the return type of such a function that returns nothing.
  • The void type is not JavaScript. It’s a TypeScript keyword used to declare return types of functions. Remember, it’s an indication that a function’s returned value isn’t meant to be used, not a value that can itself be returned

Never Returns

  • Some functions not only don’t return a value, but aren’t meant to return at all. Never-returning functions are those that always throw an error or run an infinite loop (hopefully intentionally!).

Arrays

TypeScript respects the best practice of keeping to one data type per array by remembering what type of data is initially inside an array, and only allowing the array to operate on that kind of data.

Array Types

  • The type annotation for an array requires the type of elements in the array followed by a [].
let arrayOfNumbers: number[];

Union-Type Arrays

  • You can use a union type to indicate that each element of an array can be one of multiple select types.
  • TypeScript will understand from an array’s declaration that it is a union-type array if it contains more than one type of element. In other words, the type of an array’s elements is the union of all possible types for elements in the array.
// Type is (string | undefined)[]
const namesMaybe = [ "Aqualtune", "Blenda", undefined ];

Multidimensional Arrays

  • A 2D array, or an array of arrays, will have two “[]”s.
let arrayOfArraysOfNumbers: number[][];

Tuples

  • JavaScript arrays may be any size in theory, it is sometimes useful to use an array of a fixed size.
  • Tuple arrays have a specific known type at each index
let yearAndWarrior: [number, string];
yearAndWarrior = [530, "Tomyris"]; // Ok

Interfaces

  • Interfaces are another way to declare an object shape with an associated name.
  • Interface types can also have properties that are themselves interface types (or object types).
type Poet = {
  born: number;
  name: string;
};

interface Poet {
  born: number;
  name: string;
}
  • You can indicate an interface’s property is optional by including a ? before the : in its type annotation.
  • TypeScript allows you to add a readonly modifier before a property name to indicate that once set, that property should not be set to a different value.

Functions and Methods

Method syntax
declaring that a member of the interface is a function intended to be called as a member of the object, like member(): void
Propery syntax
declaring that a member of the interface is equal to a standalone function, like member: () => void
interface HasBothFunctionTypes {
  property: () => string;
  method(): string;
}

Index Signatures

  • TypeScript provides a syntax called an index signature to indicate that an interface’s objects are allowed to take in any key and give back a certain type under that key.
  • An index signature looks like a regular property definition but with a type after the key, and array brackets surrounding them.
interface WordCounts {
  [i: string]: number;
}
  • Index signatures are convenient for assigning values to an object but aren’t completely type safe. They indicate that an object should give back a value no matter what property is being accessed.
  • When possible, if you’re looking to store key-value pairs and the keys aren’t known ahead of time, it is generally safer to use a Map. Its .get method always returns a type with | undefined to indicate that the key might not exist.
  • Interfaces are able to include explicitly named properties and catchall string index signatures, with one catch: each named property’s type must be assignable to its catchall index signature’s type.

Interface Extensions

  • TypeScript allows an interface to extend another interface, which declares it as copying all the members of another. An interface may be marked as extending another interface by adding the extends keyword after its name (the “derived” interface), followed by the name of the interface to extend (the “base” interface).
interface Writing {
  title: string
}

interface Novella extends Writing {
  pages: number
}
  • Interfaces in TypeScript are allowed to be declared as extending multiple other interfaces.
interface GivesBothAndEither extends GivesNumber, GivesString {
  giveEither(): number | string
}

Interface Merging

  • Interface merging means if two interfaces are declared in the same scope with the same name, they’ll join into one bigger interface under that name with all declared fields.
interface Window {
  myEnvironmentVariable: string
}

window.myEnvironmentVariable // Type: string

Classes

  • Class constructors are treated like typical class methods with regards to their parameters.
class Greeted {
  constructor(message: string) {
    console.log(`As I always say: ${message}!`)
  }
}

new Greeted("take chances, make mistakes, get messy")
new Greeted() // Error: Expected 1 arguments, but got 0.
  • Classes may declare a property as read-only by adding the readonly keyword before its declaration name.
  • Much like interfaces, classes may declare a property as optional by adding a ? after its declaration name.
class MissingInitializer {
  property?: string
  readonly text: string
}

Class Properties

  • To read from or write to a property on a class in TypeScript, it must be explicitly declared in the class.
  • Class properties are declared using the same syntax as interfaces: their name followed optionally by a type annotation.
class FieldTrip {
  destination: string;
}

Classes as Types

  • Classes are relatively unique in the type system in that a class declaration creates both a runtime value - the class itself as well as a type that can be used in type annotations.
class Teacher {
  sayHello() {
    console.log("Take chances, make mistakes, get messy!")
  }
}

let teacher: Teacher
teacher = new Teacher() // Ok
  • TypeScript will consider any object type that happens to include all the same members of a class to be assignable to the class. This is because TypeScript’s structural typing cares only about the shape of objects, not how they’re declared.

Classes and Interfaces

  • TypeScript allows a class to declare its instances as adhering to an interface by adding the implements keyword.
  • Doing so indicates to TypeScript that instances of the class should be assignable to each of those interfaces.
interface Learner {
  name: string
  study(hours: number): void
}

class Student implements Learner {
  name: string

  constructor(name: string) {
    this.name = name
  }

  study(hours: number) {
  }
}
  • Classes in TypeScript are allowed to be declared as implementing multiple interfaces.
class ReportCard implements Graded, Reporter {
  grades: number[]
}

Type Modifiers

Top Types

any

  • A top type, or universal type, is a type that can represent any possible value in a system. Values of all other types can be provided to a location whose type is a top type.
  • The any type can act as a top type, in that any type can be provided to a location of type any.
let anyValue: any
anyValue = "Lucille Ball" // Ok
anyValue = 123; // Ok
console.log(anyValue); // Ok
  • The problem with any is that it explicitly tells TypeScript not to perform type checking on that value’s assignability or members.

unknown

  • If you want to indicate that a value can be anything, the unknown type is much safer. The unknown type in TypeScript is its true top type. unknown is similar to any in that all objects may be passed to locations of type unknown.
  • The unknown type in TypeScript is its true top type. unknown is similar to any in that all objects may be passed to locations of type unknown.
  • TypeScript does not allow directly accessing properties of unknown typed values. The only way TypeScript will allow code to access members on a name of type unknown is if the value’s type is narrowed, such as using instanceof or typeof, or with a type assertion.

Type Predicates

  • TypeScript has a special syntax for functions that return a boolean meant to indicate whether an argument is a particular type. This is referred to as a type predicate.
  • Type predicates are commonly used to indicate whether an argument passed in as a parameter is a more specific type than the parameter’s.
function isNumberOrString(value: unknown): value is number | string {
  return ["number", "string"].includes(typeof value)
}
  • You can think of a type predicate as returning not just a boolean, but also an indication that the argument was that more specific type.

Type Operators

keyof

  • TypeScript provides a keyof operator that takes in an existing type and gives back a union of all the keys allowed on that type. Place it in front of the name of a type wherever you might use a type, such as a type annotation.
type Ratings = {
  audience: number
  critic: number
}

function getCountKeyof(ratings: Ratings, key: keyof Ratings): number {
  return ratings[key] // Ok
}

const ratings: Ratings = { audience: 66, critic: 84 }
getCountKeyof(ratings, "audience") // Ok

typeof

  • Another type operator provided by TypeScript is typeof. It gives back the type of a provided value. This can be useful if the value’s type would be annoyingly complex to write manually.
const original = { medium: "movie", title: "Mean Girls" }
let adaptation: typeof original
const ratings = { imdb: 8.4, metacritic: 82 }
function logRating(key: keyof typeof ratings) {
  console.log(ratings[key])
}
logRating("imdb") // Ok
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment