Way of the Script — React + Redux + Typescript: Part 2— Counter

Hail!,
Friends, warriors and shield maidens,

I hope you’ve all kept your wits sharp, but your swords sharper, for we are in for another type wielding session once again. This time things are going to start to get a bit more interesting. Once we get to part 4, that is when things will really start heating up!

So keep your tankards full, your hammers high, tighten your boots and off we go!

Source: https://alehorn.com/2016/12/29/how-to-toast-like-a-viking/

Before we embark on our quest, here be the treasures that we will obtain by the end of our adventure.

Quest Goals

  • Evolve the basic Counter example to a Container / Presentation style application.
  • More Typescript goodness
    - Difference between Interface types and Type alias
    - Advanced types: Union and Intersection types
    - Declaring functions in Interfaces

Clone the repo from this branch to follow along — CLICK HERE

  • Install dependencies with yarn install or npm install
  • Run project with yarn start or npm start
  • Go to localhost:8080 in the browser

Counter Application Example

How many can you handle :> ?

Running the sample application, you should be greeted with the traditional, yet boring counter example (we all have to start from somewhere).

There is not much difference here compared to a regular React JS application counter, instead of displaying the whole source code, I will provide a direct link to the file for reference, and only display the code snippets which i feel are worth discussing.

NOTE — Line numbers will be related to my Github source code

./src/components/Counter.tsx — CLICK HERE

This looks like just regular React JS syntax right? though don’t be fooled as the Gods are already smiling before us! For Typescript is already bringing us some awesome benefits baked in under the hood.

Function Argument / Parameter Checking

Typescript easily allows us to have various checks for functions we use in our code, by checking the number of arguments passed, and also their types.

This us to keep our code base cleaner by stopping any unwanted parameters or arguments being passed around, for Odin sees all and none may pass without his acceptance.

On line 26 (of Source Code), add an Integer to the this.handleIncrease(1) in the button onClick event, and be welcomed with the error displayed below.

You shall not pass!

The TS compiler keeps track of the number of expected arguments a function uses, along with checking their types. While this may not be a life saving change on the battlefield, it will certainly help us on our path to maintaining clean code.

Leaving that argument in the function, jump to line 37– 44 (of Source Code), where the handleIncrease function is defined, add the ‘num’ parameter, along with the expected type as ‘number’.

Your code should look like this :)

The TS compiler will now be happy, and all errors will be gone.

If we were to change the expected type to a string like so:

The compiler would once again throw another error where we are calling the function.

No Integers allowed here!

With minimal noise added to our application, we have already been able to greatly reap some of the benefits that Typescript has to offer, and yet we are at just the start of our Quest!

There are yet many treasures to be discovered the deeper we go, keep close friend, as we journey deeper, for you have yet much more to learn.

Your code should now currently be like this — CLICK HERE

Source: https://www.artstation.com/artwork/OxZrb

Refactor — Container & Presentation Style Components

Lets refactor the application now to a “Container / Presentation” style React app, your code should look somewhat like this — CLICK HERE

The src/containers/CounterContainer.tsx should not be anything to throw you off guard, so lets jump straight to the Presentation component described below.

/src/components/CounterView.tsx — CLICK HERE

I will focus on explaining lines 4–15 as the rest of the component is pretty standard to what we have already covered.

Here we are coming 3 new things which we have yet to discover:

  • type alias: type ComponentProps (Line 9 of the above gist).
  • Intersection types: This is the '&’ symbol, we used it CounterContainerState & CounterViewProps (line 9 of the above Gist).
  • Declaring functions in Interface: Line 2–6 of the above gist

interface types vs type aliases

Before looking further into the above code, I would like to explain quickly the differences between the two, these two types (interface and type aliases) have many similarities for object type literals and are usually interchangeable.

Though the interface type tends to offer more features, making them usually more favored over the type aliases. Some of the main differences I would like to point our are:

  • Interfaces can use ‘extends’ or the ‘implements’ clause, type alias can’t.
  • Interfaces can have multiple merged declarations, while type alias cant

Below I have refactored the Interface from the above gist (Line 2–6) to use a type alias.

type CounterViewProps = {
handleDecrease: () => void;
handleIncrease: (num: number) => void;
handleDisplayError: () => void;
}

The program should run fine, and not throw any errors, as you can see its quite easy to interchange the two.

Reverting the code back to using an interface, we notice that CounterViewProps in CounterView.tsx we are defining functions as part of the Component props, which have been passed down by CounterContainer.tsx.

interface CounterViewProps {
handleDecrease: () => void;
handleIncrease: (num: number) => void;
handleDisplayError: () => void;
}

If we were to remove one of these from our interface, we would get errors in 2 locations:

  1. CounterView.tsx — Where we are using it
Function does not exist here!

The Typescript compiler is telling us that we are using a function that has not been defined to be used in this Component, so we are provided with a visual error from the Typescript compiler.

2. CounterContainer.tsx — Where it has been passed down from

This function is not being used here!

Our container component is displaying an error, because we have passed down a function as a prop into CounterView.tsx, which is not defined in the CounterViewProps interface.

Next we will look into how we declare functions in our Interfaces.

Declaring functions in an Interface

Looking at our CounterViewProps interface in CounterView.tsx-HERE

interface CounterViewProps {
handleDecrease: () => void;
handleIncrease: (num: number) => void;
handleDisplayError: () => void;
}

We can see that we declared all our functions to return void.

It is convention to set the return as void, rather than null on functions which are not returning anything.

We also stated that handleIncrease should have 1 argument called ‘num’ of a ‘number’ type, If we were to define the function as follows;
handleIncrease: (num: string) => void;

Once again we would get 2 errors in both the CounterContainer.tsx and CounterView.tsx as follows:

  1. CounterContainer.tsx
You shall not pass!

Because we had defined the function ashandleIncrease(num: number) on line 28 in CounterContainer.tsx, it is stating that string is not acceptable.

2. CounterView.tsx

We defined in the interface thathandleIncrease: (num: string) => void; should have a string as a parameter, but on line 25 in CounterView.tsx, we are passing an integer. If we return the type definition back to number, all errors will then vanish.

Intersection Types

These allow us to combine multiple types into one, and can be used for several use cases, specifically adding together existing types to get a single type, which has all the features that we may need.

In the CounterView.tsx-HERE on line 12 you see I have used the ‘type alias’ named ComponentProps to combine CounterContainerState and CounterViewProps like so:

type ComponentProps = CounterContainerState & CounterViewProps

This is combining the CounterContainerState interface which we defined in CounterContainer.tsx and the Interface that we have defined locally in our CounterView.tsx into a single type.

We could also do this another way using an interface, on line 14 that I commented out a way of creating ComponentProps using the interface with extends. (We will talk about extends in the next Guide)

I simply did this to show how you could use both methods, which you prefer is up to you.

interface ComponentProps extends CounterContainerState, CounterViewProps {}

It is not required to merge both types into a single type, but I find that it keeps our Component declaration much cleaner. Currently in our CounterView.tsx we create out React Stateless component in this manner: (line 17)

Declare a single, combined Type

If we didnt want to decare a single type as above, we could just do:

Declare all Interfaces individually

Which way you want to do it, is completely up to you, I personally like to declare a single type, in case in larger applications I may using multiple types in a single component, and believe it it may look much cleaner using the combined type method (Intersection types).

The rest of this application should be pretty explanatory, though if you need any clarification, please feel free to sound the Horn of Valhalla, and I shall heed your call!

Please comment if this guide was difficult to understand or follow, as I am trying my best to make this as simple as possible, yet at the same time I am trying to give a clear understanding of how Typescript works.

Till then my brothers and sisters, hold thy hammer high!

Source: https://moviepilot.com/posts/2441879

In case you have missed the previous guide, you can find the collection here, along with links, to allow you to find thy path.

Guides