Learn Typescript – Real Life Examples

In here we’ll learn typescript from real life examples, Actually situations, real scenarios that demonstrate how typescript will help you developing your code. in addition, it will help you understand the errors hell-hole that typescript is vomiting on you whenever there’s a small error in your code. Let’s dive in.
In this article we’ll see code examples but also errors, so it will be easier for us to read future errors or at least it will be easier for us to recognize errors. The really good thing about this article is that we’ll talk about what typescript SAYS and what typescript DOESN’T says about errors within our code (so we’ll gain the experience understand typescript).

#1 Assign Wrong Type

The first example is easy, simple just to get started. We’ll use react state to define a new data, However neither javascript or reactjs allow us to make sure that the type of the data will be the expected data! Hence why we’ll use typescript, we’ll apply the wrong data type and see what happens. here’s the code:

import { useState } from "react";

export default function MyComponent() {
    const [number, setNumber] = useState<number>([])

    return <div>
        Learn Typescript from real life examples
    </div>;
}

Above code we define a number as the type we want “setNumber” and “number” to be, However in this case we by mistake define the initial value of the state as empty array, so we get the following typescript error:

Argument of type ‘never[]’ is not assignable to parameter of type ‘number | (() => number)’.ts(2345)

“Just tell me you expected a number but received array” – the people

Let’s fix the typescript error with the following default value of “0” rather than empty array (or change the type definition to suit an empty array rather than a number):

const [number, setNumber] = useState<number>(0)

#2 Protect Complex Object

In below example we have a useState array of objects, it means we have a list of objects that we push into array. Those objects have specific data structure that we’ll define with typescript “type”, This will help us to make sure that whenever we interact with the array and the objects, we’ll have compilation (wow) and we’ll have warning if we try to insert and push object that is incomplete (partial, missing keys, have wrong values etc).
Let’s take a look whats wrong with the following code:

import { useEffect, useState } from "react";

type codeCharType = {
    index: number
    char: string
    state: string
}

export default function MyComponent() {
    const [code, setCode] = useState<codeCharType[]>([])
    
    useEffect(() => {
        console.log('Component Constructor');

        // update code data structure with: setCode
        setCode((previousCode) => {
            return previousCode.map(() => {
                return {index: 4 , char: 'A'}
            })
        });

    }, []);

    return <div>
        Learn Typescript from real life examples
    </div>;
}

Yes, You right! we try to push a new object that only have keys of “index” and “char”, however we are missing a key and it’s value! here it’s easier to identify the problem since the code is short, it doesn’t do much beside one action etc. but in large file with lots of actions we are going to have problems identify issues like this and this is where typescript come’s in hand. Let’s take a look at the error typescript provide us:

Argument of type ‘(previousCode: codeCharType[]) => { index: number; char: string; }[]’ is not assignable to parameter of type ‘SetStateAction<codeCharType[]>’.
Type ‘(previousCode: codeCharType[]) => { index: number; char: string; }[]’ is not assignable to type ‘(prevState: codeCharType[]) => codeCharType[]’.
Type ‘{ index: number; char: string; }[]’ is not assignable to type ‘codeCharType[]’.
Property ‘state’ is missing in type ‘{ index: number; char: string; }’ but required in type ‘codeCharType’.ts(2345)

“Easy to understand” – typescript.

Above error, Solution or answer is at the end of the error, which says “state” is missing in type of “X” but required in type codeCharType (that we defined above). However, typescript doesn’t tell us here that it refers to what is being returned! it doesn’t tell us that it refers to the return objected! Now, again, it’s easier to relate the error to the execs location of the error, even if typescript doesn’t really tell us where the problem is.

Let’s fix above error and add state to the object we modify:

return {index: 4 , char: 'A', state: 'active'}

Above should fix the error! since we return the expected object to the array.

#3 Typescript Won’t Warn For ReactJS

The next simple example is of a counter that is triggered with a pressed of a button and increment a variable. However, because of Reactjs approach to rendering and implementation of async actions (side effects like interval counter) ReactJS will not trigger update to the counter and the counter will stay the same! Typescript however, won’t be able to detect this issue and will not warm you about it. There’s noting wrong about ReactJS or typescript here, it’s how two different things meet each other and failing expecting of software engineer which can result with unexpected behaviour. Let’s take a look at the expected Javascript code (the code that work):

continueCounter = 1;
const intervalContinueCounter = setInterval(() => {
  console.log('interval called once: ', continueCounter);
  continueCounter = continueCounter + 1;

  // exist point - so interval wont run forever
  if (continueCounter > 10) {
    clearInterval(intervalContinueCounter);
  }
}, 1000); // every 1 sec bump the counter

Above code example will simply trigger and bump the continueCounter variable, and it will console log every 1 second a new increment number. We can see in the photo the next example in which we write in Reactjs and the code won’t work! and typescript won’t give us any information about it and won’t help us solve the issue. The reason we talk about it and test it and test the code and typescript in such way is because we need to learn the limitation of a superset. Otherwise we’ll have unrealistic expectation of bug-free code.

The next example is Reactjs implementation with typescript as a superset an no warning whether if the code will work or in this case, we know, it won’t work:

import React, { useState } from 'react';

export const App = () => {
  const [continueCounter, setContinueCounter] = useState<number>(1);

  function play() {
    const intervalContinueCounter = setInterval(() => {
      console.log('interval called once: ', continueCounter);
      setContinueCounter(continueCounter + 1);

      // exist point - so interval wont run forever
      if (continueCounter > 10) {
        clearInterval(intervalContinueCounter);
      }
    }, 1000); // every 1 sec bump the counter
  }

  return (
    <div className="p-2">
      <h1>React TSX Starter</h1>
      <div>containing:</div>
      <ul>
        <li>React</li>
        <li>Twitter Bootstrap</li>
        <li>Basic Functional Components App and ButtonCounter</li>
      </ul>
      <button onClick={play}>test {continueCounter}</button>
    </div>
  );
};

Task: play around with above code and try to make it work, hint: you should use react hooks like useState, useEffect or something else – Here’s a stackblitz for you in reactjs and typescript.

#4 Next Item Possibility Undefined

in the following example we have a list of user objects inside an array, We use a filter function to go over all the objects inside the array and check if the username in the next object starts with the letter “e” (for sake of our experiment, you’ll face a more realistic case in the future). Sine we user filter, and then print the list of users base on the results provided by “filter” we might have empty objects as our result which yield the following error:

‘nextUser’ is possibly ‘undefined’.ts(18048)

Let’s check the code (i’d recommended you to copy the code to a typescript project and see the error yourself and offer solutions before you continue reading):

import React from "react";

// Define the User type
type User = {
  id: number;
  username: string;
  isActive: boolean;
};

const users: User[] = [
  { id: 1, username: 'Alice', isActive: false },
  { id: 2, username: 'Bob', isActive: false },
  { id: 3, username: 'Eve', isActive: false },
];

const detectNextUserStartsWithE = (users: User[]): User | undefined => {
  // Filter users whose usernames start with 'E'
  const filteredUsers = users.filter(user => user.username.startsWith('E'));

  // Return the first user if available
  return filteredUsers[0];
};

const App: React.FC = () => {
  const nextUser = detectNextUserStartsWithE(users);

  // The following line might cause a TypeScript error since `nextUser` could be `undefined`
  nextUser.isActive = true;

  return (<div>
      <h1>User Status</h1>
      {users.map(user => (
        <div key={user.id}>
          {user.username}: {user.isActive ? 'Active' : 'Inactive'}
        </div>
      ))}
    </div>
  );
};

export default App;

One solution would be to do manual type checking or in this case, check that the object exists:

  if (nextUser) {
    nextUser.isActive = true;
  }

#5 – Define React useState Functions

In react we’ll be creating a lot of useState functions, for example “counter” and “setCounter” and many more, and sometime we’ll want to forward the functions from parent to a child component, However, in typescript we’ll want to define the type of such newly created function, here’s how:


// MyChildComponent.tsx
import React from 'react';
interface GameBoardI {
    setTotalActions: React.Dispatch<React.SetStateAction<number>> // setTotalActions is a useState Function define in parent
}

const GameBoard = ({
        setTotalActions
} : GameBoardI) => {

    return <div onClick={() => setTotalActions(50)}>test</div>
}

export default GameBoard;

Example of how the parent will forward the function:

<GameBoard setTotalActions={setTotalActions} /> // forward a useState function

#6 – Soon

Come back soon for more examples!

Leave a Reply

Your email address will not be published. Required fields are marked *

All rights reserved 2024 ©