4 useful Typescript tricks

Niccolo
3 min readOct 6, 2019

Typescript is a godsend. It is very easy to get started with and for most developers there is no way back once they get the hang of it. Sometimes it can get pretty advanced and intimidating though.

This is why I decided to share 4 of my favourite typescript tips and tricks you might have needed in the past. Some are super basic, some are bit more advanced.

  1. react higher-order components
  2. smarter constructor
  3. type checking functions
  4. excluding object types
Photo by Amador Loureiro on Unsplash

Higher-order Component

In React higher order components (HOC) are very useful tools. Generally they are used to wrap some layout or functionality to some other components. They are simply functions that return another component, basically the same pattern as decorators.

In typescript it can be confusing how to write them maintaining the right after wrapping the original component. Here you are:

import React from 'react'

function withLayout<P extends object>(WrappedComponent: React.ComponentType<P>) {
return (props: P) => (
<div id='app'>
<Header/>
<WrappedComponent {...props}/>
<Footer/>
</div>
);
}

Smarter constructors

Let’s start by the basic building block this is based on. It’s a basic Javascript trick, not a typescript exclusive at first.

class Pizza {
slices: number
name: string

constructor(init) {
Object.assign(this, init)
}
}

const pizza = new Pizza({
slices: 8,
name: 'Margherita',
})

What is happening here? With the super handy Object.assign simply assigns the object to the class. This is super handy when classes have many constructor parameters. But this is NOT type safe as your IDE/Editor will tell you. How do we fix this?

import { NonFunctionKeys } from 'utility-types'

class Pizza {
slices!: number
name?: string

constructor(init: Pick<Pizza, NonFunctionKeys<Pizza>>) {
Object.assign(this, init)
}

eat() {
this.slices = 0
}
}

const pizza = new Pizza({
slices: 8,
name: 'Margherita',
})

Let me explain what happens:
This leverages the awesome utility-types package. We first take all the keys that are not a function, so we don’t overwrite the eat method of the class. Then we pick those from the general Pizza type.

This means that slices will be required, while name will be optional, as they are defined.

Type-checking Functions

Did you know you can write functions to tell typescript what type something is? This is awesome!

Suppose we have the following interfaces

interface Food {
name: string
}

interface Pasta extends Food {
type: 'Spaghetti' | 'Fusilli'
}

interface Pizza extends Food {
slices: number
}

Now we could write a cook function that accepts both Pasta and Pizza. Typescript itself cannot differentiate between the too.

function cook(what: Food) {
if(what === Pizza) ????
}

Fortunately there is a nice solution built into typescript.

function isPizza(x: Food | Pizza): x is Pizza {
return x.hasOwnProperty('slices')
}

function isPasta(x: Food | Pasta): x is Pasta {
return x.hasOwnProperty('type')
}


function cook(plate: Food) {
if (isPizza(plate)) {
// Plate is now of type Pizza
putInTheOven(plate)
}
if (isPasta(plate)) {
// Plate is now of type Pasta
putInThePan(plate)
}
}

Here we define two functions that return x is Sometype and return a boolean value based on the input. It's up to you of course to define it properly, but this can be very useful in various situations.

Excluding object types

type Sqlite = {
type: 'sqlite',
database: string,
}

type PostgreSQL = {
type: 'postgresql',
database: string,
host: string,
post?: number
}

type PossibleConfigs = Sqlite | PostgreSQL

function initialize(config: PossibleConfigs) {}

This might look like a simple one, but I often see people putting those sorts of types all into the same interface. By separating the different type of objects you make sure that they are safe. Also the autocomplete will thank you.

--

--