Viet Nguyen Blog

Getting Started with TypeScript and NextJs

April 23, 2019

DevJavaScriptReactTypeScript

TL;DR I am an amateur when it comes to type systems as I came from a JavaScript and PHP background, so this new must-type-everything-before-running is tedious and new to me. Learned a few things and made a small guide to Typescript as it pertains to writing React code.

So I was always curious about TypeScript and I wanted to make something with it to see all the gains I can make to my coding if I were to add it in to a project. Typescript is a typed superset of Javascript and it compiles to plain Javascript. So prior knowledge of Javascript is required, first and foremost.

On top of the javascript that you are coding, you have to declare a type for everything. At first, this was super tedious to me. Things like:

const str = 'hello world'
let num = 8
let isClicked = true
let dontWantToType = 'whatever'
let things = [ 'ball', 'string', 'wool' ]
let numList = [ 1, 2, 3, 4 ]
let notSameThings = [ 'ball', 5, true ]
let obj = { stuff: 'stuff' }
const doThis = (things) => calculateStuff() + things

turn into

const str:string = 'hello world'
let num:number = 8
let isClicked:boolean = true
let dontWantToType:any = 'whatever'
let things:string[] = [ 'ball', 'string', 'wool' ]
let numList:number[] = [ 1, 2, 3, 4 ]
let notSameThings:[string, number, boolean] = [ 'ball', 5, true ]
let obj:object = { stuff: 'stuff' }
const doThis = (things:string):string => calculateStuff() + things

TypeScript won’t yell at you for missing a type declaration though (unless you made your IDE yell at you). No type declaration means no type checking and all the good stuff Typescript comes with so it would benefit you to type everything.

Some of the type declarations are pretty straightforward for your variable declarations. Basically, after declaring the variable you just follow the variable name with a colon and the type that the variable is going to be.

The any type is your get-out-of-jail-free card but you should only use it if you have no idea what the type is going to be. This is probably the type you will be using the most if adding TypeScript to an existing project (for API requests and the like).

The notSameThings variable is typed as a Tuple. A tuple type is a way to type an array that has a fixed number of elements that are all not the same type.

For functions, if there are arguments, you have to declare a type for each argument. If the argument returns a value, you also have to declare a type for the return value.

If you have a long list of arguments, it can become too cumbersome to declare types. You can use interfaces for that. Interfaces are like objects that declare types for multiple things. For instance:

interface Things {
    ball: string
    wool: string
    id: string
}

and then you can use it as so:

const doThis = (things:Things):string => calculateStuff() + things.ball

You can also denote if a property in an interface is optional by adding a ? to the property name right before the colon. Like so:

interface Things {
    ball: string
    wool?: string
    id?: string
}

This is as deep as I got into TypeScript in order to start integrating or building stuff. You can look at the official docs here.

Let’s jump into seeing if our new-found prowess in typing can help us with React.

I started with the boilerplate I made. Next, you should install TypeScript and the types that the other libraries would require. Luckily, since we are arriving to the TypeScript movement late, there have been type libraries already written for each library we are using. If you are using the boilerplate, you need to do this:

npm install --save @types/next @types/react @types/rebass @types/styled-components @zeit/next-typescript typescript

Make a new file at the root of the project and call it next.config.js

const withTypescript = require('@zeit/next-typescript')
module.exports = withTypescript()

Make another new file at the root of the project and call it tsconfig.json

{
    "compilerOptions": {
        "allowJs": true,
        "allowSyntheticDefaultImports": true,
        "jsx": "preserve",
        "lib": ["dom", "es2017"],
        "module": "esnext",
        "moduleResolution": "node",
        "noEmit": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "preserveConstEnums": true,
        "removeComments": false,
        "skipLibCheck": true,
        "sourceMap": true,
        "strict": true,
        "target": "esnext"
    },
    "include": [
        "components",
        "pages"
    ],
    "exclude": [
        "node_modules"
    ]
}

The include section is for folders that will have TypeScript that you wanna compile down to javascript. The exclude is for what you want to exclude for compilation.

Last new file at the root of the project and call it .babelrc

{
    "presets": [
        "next/babel",
        "@zeit/next-typescript/babel"
    ]
}

and now you are ready to integrate TypeScript into your react code.

Here is an example of what a component that’s using styled components could look like.

import { Flex as Base } from 'rebass'
import styled from 'styled-components'

interface FlexProps {
    boxShadow?: string
    height?: string
    center?: boolean
    border?: string
}

const Flex = styled(Base)<FlexProps>`
    ${props => (props.boxShadow ? `box-shadow: ${props.boxShadow};` : null)}
    ${props => (props.height ? `height: ${props.height};` : null)}
    ${props => (props.center ? `justify-content: center; align-items: center;` : null)}
    ${props => (props.border ? `border: ${props.border};` : null)}
`

export default (props:any) => (
    <Flex {...props}>
        {props.children}
    </Flex>
)

Pretty simple. Adding an interface will make sure that the props coming in will only be those props. As for why I added the any type to the props on the export function, Rebass has more props that can be added to the Flex component that will allow it to be do more (i.e. m for margin, p for padding) and I have yet to account for that, so get-out-of-jail-free AKA the any type.

Here is an example of using async/await with TypeScript:

const fetchStuff = async (url:string):Promise<object> => {
    const res:any = await fetch(url)
    const data:object = await res.json()
    return data
}

Declaring a function to be an async function will in turn make the function into a Promise so we can declare that a promise will be returned and since we are returning a value back we can type the returned promise as an object when it resolves.

I am sure I am just scratching the surface of TypeScript but I am enjoying the process and learning as I go. You can look at a recent (at the time of this writing) project that I was able to incorporate Typescript into.


Written by Viet Nguyen. Senior Software Engineer residing in Orlando, FL.What I'm Doing Now