Terminology plays an important role in understanding TypeScript and JavaScript. Terminology helps us to describe a complex context and prevents us from confusion when talking about code.

As important as it is to know the control structures of a programming language, it is just as important to be able to name their contexts and surroundings. Using the right vocabulary is particularly effective in code reviews as it supports us to put our thoughts into words.

  1. 1. Algorithms & Functions
    1. 1.1. Algorithm
    2. 1.2. Declarative Programming
    3. 1.3. Imperative Programming
    4. 1.4. Function Declaration
    5. 1.5. Function Expression
    6. 1.6. Referentially Opaque Expressions
    7. 1.7. Referentially Transparent Expressions
    8. 1.8. Block Scope
    9. 1.9. Function Scope
    10. 1.10. Deterministic Functions
    11. 1.11. Identity Function
    12. 1.12. Pure Functions
  2. 2. TypeScript
    1. 2.1. Ambient Context
    2. 2.2. Array Destructuring
    3. 2.3. Collective Types
    4. 2.4. Compiler
    5. 2.5. Discriminated Unions
    6. 2.6. Literal Types
    7. 2.7. Module
    8. 2.8. Non-primitive types
    9. 2.9. Primitive Types
    10. 2.10. String Literal Type
    11. 2.11. Tagged template
    12. 2.12. Template Literal Type
    13. 2.13. Template Literal
    14. 2.14. Transpiler
    15. 2.15. Tuple Type
    16. 2.16. Type Annotation
    17. 2.17. Type Argument Inference
    18. 2.18. Type Argument
    19. 2.19. Type Inference
    20. 2.20. Type Variable
    21. 2.21. Union Types

Algorithms & Functions


Algorithm

An algorithm is a set of instructions to solve specific problems or to perform a computation. In TypeScript algorithms can be implemented with functions.

Algorithm Characteristics

Algorithms have defining characteristics such as:


Declarative Programming

Declarative programming is a programming paradigm that defines the desired state of an application without explicitly listing statements that must be executed. In a nutshell, declarative programming defines an application’s execution from finish to start.

Example:


Imperative Programming

Imperative programming is a programming paradigm that uses statements to change an application’s state. In a nutshell, imperative programming defines a set of instructions from start to finish.

Example:


Function Declaration

A function declaration gets hoisted and is written in the following way:

1
2
3
function myFunction(): number {
return 1337;
}

Function Expression

A function expression is part of an assignment and does not get hoisted:

1
2
3
const myFunction = function(): number {
return 1337;
}

Referentially Opaque Expressions

An expression is referentially transparent when it cannot be replaced with its return value.

Example:

1
2
3
4
5
function today(): string {
return new Date().toISOString();
}

const isoDate = today();

At the time of writing the execution of today() returned '2021-09-22T12:45:25.657Z'. This result will change over time, so we cannot replace const isoDate = today() with const isoDate = '2021-09-22T12:45:25.657Z' which makes this expression referentially opaque.


Referentially Transparent Expressions

An expression is referentially transparent when it can be replaced with its return value.

Example:

1
2
3
4
5
function sayHello(): string {
return `Hello!`;
}

const message = sayHello();

The sayHello function always returns the same text, so we can safely replace our expression with const message = Hello! which makes it referentially transparent.


Block Scope

To enforce block-scoping in JavaScript, the let keyword can be used. It makes variables inaccessible from the outside of their blocks:

1
2
3
4
5
6
7
8
9
function myFunction(): void {
if (true) {
let myNumber = 1337;
}
// Will throw a `ReferenceError` because `myNumber` is not defined
return myNumber;
}

console.log(myFunction()); // Causes an uncaught `ReferenceError`

Function Scope

By default, JavaScript is function scoped which means that variables are accessible within a function:

1
2
3
4
5
6
7
8
9
function myFunction(): void {
if (true) {
var myNumber = 1337;
}
// Variable `myNumber` can be accessed within the function although it is being used outside of its conditional if-block
return myNumber;
}

console.log(myFunction()); // 1337

Deterministic Functions

A deterministic function will always produce the same output given the same input. This makes the output of a deterministic function predictable as it does not rely on a dynamic state.


Identity Function

An identity function returns the identical value that was given to it:

Example:

1
2
3
function identityFunction(text: string): string {
return text;
}

Pure Functions

Pure functions are a subset of deterministic functions. A pure function always produces the same result given a particular input. In addition, it does not cause side effects by avoiding I/O operations like printing to the console or writing to the disk.

A pure function does not mutate its passed parameters and is referentially transparent.


TypeScript


Ambient Context

By default, the TypeScript compiler does not know in which runtime environment (for instance Node.js v16, Electron v16, Chrome v94) our code will be executed later. That’s why we can help the compiler knowing that by defining an ambience / ambient context.

Example: If you run your code in an environment where there is a “world” object that TypeScript does not know about, you can define that context using declare var:

1
2
3
4
5
declare var world: {
name: string;
};

console.log(world.name);

Array Destructuring

1
const [bart, homer, marge] = ['Bart', 'Homer', 'Marge'];

Collective Types

A collective type unites all literals of its kind. Collective types can be used to widen a type.

Example: The type number is a collective type of all integers as it includes different integer values:

1
const numberCollective: number = 72;

Compiler

Compilers transform high-level programming language code to low-level machine code or some low-level intermediate representation (e.g. Bytecode in Java).


Discriminated Unions

Discriminated Unions (or tagged union types) are a form of type guards where the type is narrowed based on a shared property.

In the following example, the type of Dog and Person have a shared property called type. Depending on the value of this property, TypeScript can narrow down the type from Dog | Person to either Dog or Person:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type Dog = {
age: number;
name: string;
bark: () => void;
type: 'dog'
}

type Person = {
age: number;
name: string;
shout: () => void;
type: 'person'
}

function makeNoise(dogOrPerson: Dog | Person): void {
switch (dogOrPerson.type) {
case 'dog':
// Type is narrowed down to "Dog", so we can "bark":
dogOrPerson.bark();
break;
case 'person':
// Type is narrowed down to "Person", so we can "shout":
dogOrPerson.shout();
break;
}
}

Because this allows the type to be discriminated, this technique is referred to as discriminating unions. This concept also exists in F# (Discriminated Unions in F#).


Literal Types

A literal describes a specification of a collective type. A literal can be an exact number or an exact string instead of being any number or being any string:

1
2
3
const numberLiteral: 72 = 72;
const stringLiteral: "Benny" = "Benny";
const booleanLiteral: true = true;

Module

In TypeScript a module is a file with at least one top level import or export statement.


Non-primitive types

All non-primitive data types (also composite data types) are passed by reference:


Primitive Types

All primitive data types are passed by value:

  1. bigint
  2. boolean
  3. null
  4. number
  5. string
  6. symbol
  7. undefined

TypeScript’s type system additional has the following primitives:

  1. unknown

String Literal Type

A string literal is a specific string (e.g. “click”) and a string literal type is the type describing a specific string:

1
const action: 'click' = 'click';

Tagged template

1
2
3
4
5
6
7
8
9
10
import gql from 'graphql-tag';

const query = gql`
{
user(id: 5) {
firstName
lastName
}
}
`

Template Literal Type

A template literal type is a combination of Template literal and string literal type. It is capable of resolving string interpolations within a template string to use it for strong typing:

1
2
3
4
5
type Action = 'click';

type ClickEvent = `${Action}Event`;

const myEvent: ClickEvent = 'clickEvent';

Template Literal

1
const templateLiteralString = `1 + 1 = ${1 + 1}`;

Transpiler

Transpilers transform high-level programming language code (e.g. TypeScript) into another high-level programming language code (e.g. JavaScript).


Tuple Type

A tuple can contain values of different data types and works like an array with a fixed number of elements:

1
2
type MyTupleType = [string, number];
const benny: MyTupleType = ['Benny', 34];

Note: A tuple (e.g. [number]) is different to an array (e.g. number[]).


Type Annotation

A type annotation is when you explicitly define the type that a variable can take on:

1
const x: number = 10;

Type Argument Inference

TypeScript can infer types but also type arguments. This usually happens when the arguments of your generic functions are connected to the generic type of your function. In this case TypeScript can infer the type of your type variable by inspecting the input values of your function call:

1
2
3
4
5
6
function yourGenericFunction<T>(input: T[]): number {
return input.length;
}

/** By inspecting the input of this function call, TypeScript will infer `yourGenericFunction<string>`. */
yourGenericFunction(['Benny']);

Type Argument

In the following code, <string> is the type argument (sometimes called type parameter):

1
const array = new Array<string>();

Type Inference

When there is no explicit type annotation then TypeScript will infer the type for you:

1
2
/** TypeScript infers `number` because "x" is initialized with a number and can be reassigned. */
let x = 10;
1
2
/** TypeScript infers `10` because "x" is a constant and cannot be reassigned. */
const x = 10;
1
2
/** TypeScript infers an array of `(string | number)` value types. */
const x = [10, '11'];

Type Variable

A type variable is the placeholder for a generic type in your generic code:

1
2
3
function yourGenericFunction<MyTypeVariable>(input: MyTypeVariable[]): number {
return input.length;
}

Type variables are written by using the angle brackets and defining a name for the variable (e.g. <T>). This construct is often referred to as the diamond operator because the angle brackets look like a diamond (<>, 💎).


Union Types

A union type describes a collection of types (i.e. string and number):

1
2
3
4
type MyUnionType = string | number;

const myName: MyUnionType = 'Benny';
const myAge: MyUnionType = 34;

It is called a union because it unites the amount of possible types. The term union comes from set theory where it is used when two (or more) sets are combined.