- Algorithms & Functions
- TypeScript
- Primitive Data Types
- Non-primitive data types / Composite data types
- Compiler
- Transpiler
- Discriminated Unions
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.
Algorithms & Functions
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:
- Generality: The algorithm must apply to a set of defined inputs.
- Definiteness / Uniqueness: At any point in time, there is at most one possibility of continuation. Each instruction step is well-defined.
- Deterministic: Given a particular input, the algorithm will always produce the same output.
- Effectiveness: The algorithm terminates in a finite amount of time / steps. Every execution of the algorithm delivers a result.
- Finiteness: The algorithm is described in a finite amount of steps.
- Feasibility: It should be feasible to execute the algorithm with the available resources.
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:
- Change UI behaviour by defining how to turn something off before turning it on
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:
- Change UI behaviour by defining how it should look like based on its props
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.
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.
Referentially Transparent Expressions
An expression is referentially transparent when it can be replaced with its return value.
Example:
1 |
|
The sayHello
function always returns the same text, so we can safely replace our expression with const message = Hello!
which makes it referentially transparent.
Identity Function
An identity function returns the identical value that was given to it:
Example:
1 |
|
Referentially Opaque Expressions
An expression is referentially transparent when it cannot be replaced with its return value.
Example:
1 |
|
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.
Function Declaration
A function declaration gets hoisted and is written in the following way:
1 |
|
Function Expression
A function expression is part of an assignment and does not get hoisted:
1 |
|
Function Scope
By default, JavaScript is function scoped, which means that variables are acccessible within a function:
1 |
|
Block Scope
To enforce block-scoping in JavaScript, the let
keyword can be used. It makes variables unaccessible from the outside of their blocks:
1 |
|
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 |
|
Union Types
A union type describes a collection of types (i.e. string
and number
):
1 |
|
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.
Module
In TypeScript a module is a file with at least one top level import
or export
statement.
Template literal
1 |
|
Tagged template
1 |
|
Array Destructuring
1 |
|
Type Annotation
A type annotation is when you explicitly define the type that a variable can take on:
1 |
|
Type Inference
When there is no explicit type annotation then TypeScript will infer the type for you:
1 |
|
1 |
|
1 |
|
Type Argument / Type Parameter
In the following code, <string>
is the type argument:
1 |
|
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 |
|
Type Variable
A type variable is the placeholder for a generic type in your generic code:
1 |
|
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 (<>
, 💎).
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 |
|
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 |
|
Tuple Types
A tuple can contain values of different data types and works like an array with a fixed number of elements:
1 |
|
Note: A tuple (e.g. [number]
) is different to an array (e.g. number[]
).
Primitive Data Types
All primitives are passed by value:
- bigint
- boolean
- null
- number
- string
- symbol
- undefined
Non-primitive data types / Composite data types
All non-primitive types are passed by reference:
- object (and array, which is also an object)
Compiler
Compilers transform high-level programming language code to low-level machine code or some low-level intermediate representation (e.g. Bytecode in Java).
Transpiler
Transpilers transform high-level programming language code (e.g. TypeScript) into another high-level programming language code (e.g. JavaScript).
Discriminated Unions
Discrimination Unions are a form of type guards that narrow down a type 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 |
|
Because this allows the type to be discriminated, this concept is referred to as Discriminated Unions. This concept also exists in F# (Discriminated Unions in F#).