⌨️ TypeScript types, interfaces & enums cheatsheet

Verónica Valls
Game & Frontend Development Stuff
6 min readMay 6, 2024

During the latest months, I’ve been deepening into the TypeScript world. This article is a summary and cheatsheet of my personal notes related with types, enums and interfaces.

🧒 Basic use types

Type annotations

They enable variables to be declared with specific types. These allow the compiler to check that the code adheres to these types.

const price: number

// Array of prices
const prices: number[]

// Alternative
const prices: Array<number>

Type annotations can also be added to function parameters and function’s return value using the same syntax as annotating a variable.

function getTotal(
price: number,
quantity: number,
discount: number
) : number {
// calculate & return the total price
}

Type inference

When a value is directly assigned to a variable, TypeScript infers the type from that value.

const flag = false // TS infers this is a boolean type

Some highlights on specific types

Object types

It’s represented like JS object literals, but instead of property values, property types are specified instead.

const product: { 
name: string,
price: number
}

Date: Representation of the JS Date object.

any: It avoids TS type checking on a particular variable.

unknown: This type can be used when we’re unsure of the type but want to interact with it in a strong-typed manner. We can’t interact with unknown variables, so the variable needs to be widened to a different type before any interaction.

This is useful for APIs that want to signal “this can be any value, so you must perform some type of checking before you use it”. This forces users to safely introspect returned values.

void: It represents a function’s return type where the function doesn’t return a value. Not to be confused with undefined type, where the function is expected to return an undefined value.

never: It represents something that will never occur and is typically used to specify unreachable code areas.

Optional properties

We can use a ? symbol to make a property optional rather than required.

const product: { 
name: string,
price?: number
}

Type aliases

It refers to another type and can be reused to be assigned to multiple variables.

type Product = {
name: string,
price?: number
}

const table: Product = { name: 'Black kitchen table' }
const chair: Product = { name: 'Acapulco green chair', price: 60 }

Type aliases can also be used to represent a function.

// Function containing a number param and returns nothing
type Purchase = (quantity: number) => void

Intersection type

A type alias can extend another object using the & symbol.

type Product = {
name: string,
price?: number,
purchase: Purchase
}

type Discount = {
percentage: number
}

// DiscountedProduct represents an object containing name,
// optional price, purchase function and percentage properties
type DiscountedProduct = Product & Discount

Union type

It creates a new type by joining multiple other types using the | symbol. Different type aliases or types can be used to create union types.

type Size = "S" | "M" | "L"

It’s similar to Enumerations. The difference is that union types only contain values rather than a name and a value (S, Small).

Type assertions

They’re used when TS can’t infer the type of a value. We can use as or the angle-bracket syntax.

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

// We can use this too except on .tsx files
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");

Utility types

Built-in utility types allow us to transform existing types into new types without the need of defining them from scratch.

Partial: It transforms all properties in optional, allowing us to create a partial element.

Readonly: It transforms all properties in read-only, preventing us from modifying them.

Pick: It creates a new type with just the specified properties of the initial type.

Omit: It creates a new type by removing the specified properties from the initial type.

type Product = {
name: string,
price?: number,
colour: string
}

type PartialProduct = Partial<Product>;
type ReadOnlyProduct = Readonly<Product>;
type NameAndPrice = Pick<Product, 'name' | 'price'>;
type WithoutColour= Omit<Product, 'colour'>;

const partialProduct: PartialProduct = {
name: 'Kallax library',
}

const readonlyProduct: ReadOnlyProduct = {
name: 'BILLY library with doors',
price: 100,
colour: 'black'
}

const nameAndPrice: NameAndPrice = {
name: 'BILLY library big',
price: 70,
}

const withoutColour: WithoutColour = {
name: 'BILLY library small',
price: 50,
}

👩‍🎓 Advanced use types

Index signatures

On the cases where we don’t know the names of some type’s properties, we use index signatures to describe the types of the possible values.

interface StringArray {
[index: number]: string;
}

const myArray: StringArray = getStringArray();
const secondItem = myArray[1];

Generic types

By using generic types, we capture the type of an argument in such a way that we can also use it to denote what is being returned. It’s not the same as using any where we do lose the information about what type the function returns.

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

// It can be used in two ways
let output = identity<string>("myString");
let output = identity("myString");

// Array example
function loggingIdentity<Type>(arg: Type[]): Type[] {
console.log(arg.length);
return arg;
}

keyof type

It takes an object type and produces a string or numeric literal union of its keys. Useful when combined with mapped types.

// The following type P is the same type as type P = "x" | "y":
type Point = { x: number; y: number };
type P = keyof Point;

Mapped types

It’s a generic type using a union of property keys to iterate through keys to create a new type. Usually used to create read-only type versions.

// OptionFlags takes all the Type properties and changes the values to boolean.
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};

type Features = {
darkMode: () => void;
newUserProfile: () => void;
};

type FeatureOptions = OptionsFlags<Features>;

Property modifiers

readonly

A property marked as readonly can’t be written to during type-checking although it won’t change any behaviour at runtime.

It doesn’t imply that a value is totally immutable, just that the property itself can’t be rewritten to. That is to say, readonly properties can change via aliasing.

interface Person {
name: string;
age: number;
}

interface ReadonlyPerson {
readonly name: string;
readonly age: number;
}

let writablePerson: Person = {
name: "Person McPersonface",
age: 42,
};

// works
let readonlyPerson: ReadonlyPerson = writablePerson;

console.log(readonlyPerson.age); // prints '42'
writablePerson.age++;
console.log(readonlyPerson.age); // prints '43'

By using mapping modifiers, we can remove the readonly attribute.

// Removes 'readonly' attributes from a type's properties
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property];
};

type LockedAccount = {
readonly id: string;
readonly name: string;
};

type UnlockedAccount = CreateMutable<LockedAccount>;

👩‍💻 Interfaces

Object types can be created with interface syntax, too.

interface Product {
name: string,
price?: number
}

An interface can extend another interface using the extends keyword.

interface DiscountedProduct extends Product {
percentage: number
}

An interface an also be used to represent a function.

interface Purchase {(quantity: number) : void)

interface Product {
name: string,
price?: number,
purchase: Purchase
}

💭 When should I use a type alias instead of an interface and vice versa?

Type aliases can create types that interfaces can’t, though, such as union types. Taking this exception into account, the capabilities of type aliases and interfaces for creating object types are very similar.

🍣 Enumerations

They allow us to declare a meaningful set of friendly names that a variable can be set to.

// Examples of Size enumerations
enum Size {
Small,
Medium,
Big
}

enum Size {
Small = 1
Medium = 2
Big = 3
}

enum Size {
Small = "S"
Medium = "M"
Big = "L"
}

const size = Size.small

We need to take into account that number-based enumerations aren’t as type-safe as we could expect. This does not happen when using strings.

enum Size {
Small = 1
Medium = 2
Big = 3
}

let size = Size.small

// When assigning a value not present on the enumeration,
// a type error does not happen
size = 4;

📚 Sources

Official docs: https://www.typescriptlang.org/docs/handbook/intro.html

Learn React with TypeScript:

https://www.packtpub.com/product/learn-react-with-typescript-second-edition/9781804614204

6 advanced TypeScript tricks for clean code: https://medium.com/@mvsg/6-advanced-typescript-tricks-for-clean-code-90cee774dbf3

12 TypeScript tricks por clean code: https://medium.com/@mvsg/12-typescript-tricks-for-clean-code-b23651dd0430

--

--

Verónica Valls
Game & Frontend Development Stuff

Mobile & frontend developer for real world projects. Game designer/developer for my mind’s delirium ideas. Cats & dogs dietetical and nutritional advisor.