Error in TypeScript not being caught in React due to incorrect type detection

Could you assist me in grasping the workings of TypeScript? I am currently trying to learn it but am struggling with understanding certain behaviors.

Example 1: The issue lies in the type errors not being detected, please refer to the commented message within the code.

"use client";

import axios from "axios";
import { useParams } from "next/navigation";
import { useEffect, useState } from "react";

type Recipe = {
  title: number; ///// TITLE SHOULD BE A STRING, NOT A NUMBER ////
  image: string;
};

export default function Categories() {
  const { category } = useParams();
  const [recipes, setRecipes] = useState<Recipe[]>([]); //////USING TYPE RECIPE/////

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get<Recipe[]>(  /////USING THE SAME TYPE RECIPE /////
          `https://mealtime-dkgl.onrender.com/api/recipes/${category}`
        );
        const extractedRecipes = response.data.map((recipe) => ({
          title: recipe.title,
          image: recipe.image,
        }));

        setRecipes(extractedRecipes); /////// NO ERRORS DETECTED ///////
      } catch (error) {
        console.error("Error fetching recipes", error);
      }
    };

    fetchData();
  }, [category]);

  console.log(recipes);

  return <main></main>;
}

Example 2: It appears to be functioning as expected.

"use client";

import axios from "axios";
import { useParams } from "next/navigation";
import { useEffect, useState } from "react";

type Recipe = {
  title: string;
  image: string;
};

type RecipeResponse = {
  title: number; ////// ERROR OCCURS HERE /////////
  image: string;
};
export default function categories() {
  const { category } = useParams();
  const [recipes, setRecipes] = useState<Recipe[]>([]); /////// USING TYPE RECIPE //////

  useEffect(() => {
    const fetchData = async () => {
      const response = await axios.get<RecipeResponse[]>( //// UTILIZING TYPE RECIPE RESPONSE /////
        `https://mealtime-dkgl.onrender.com/api/recipes/${category}`
      );
      const extractedRecipes = response.data.map((recipe) => ({
        title: recipe.title,
        image: recipe.image,
      }));

      setRecipes(extractedRecipes); /////ERROR DETECTED/////
    };

    fetchData();
  }, [category]);

  return <main></main>;
}

I trust that my examples are clear enough. In essence, I have observed that re-using "type Recipe" multiple times in my code leads to undetected errors.

Answer №1

When it comes to TypeScript, it only operates based on the information given to it. If you define a type and TypeScript doesn't find any conflicts with that type, it won't raise any complaints, even if the specified type is incorrect.

The Initial Scenario

Let's break down what happened in the initial scenario:

  1. You mistakenly declared the type as Recipe, stating that title is a number:
type Recipe = {
  title: number;
  image: string;
};
  1. Upon making an API call, you mentioned that the elements would all be of type Recipe. While you knew that the API would return { title: string, image: string }, TypeScript simply trusts your declaration without prior knowledge of what the API response will look like.
const response = await axios.get<Recipe[]>(...
  1. Subsequently, you process the response data.
const extractedRecipes = response.map((recipe) => ({
    title: recipe.title,
    image: recipe.image,
}));

In this case, TypeScript can deduce some information based on your declarations. Since you indicated that response is an array of Recipe, TypeScript infers that inside the map callback, recipe.title is a number and recipe.image is a string.

As a result, extractedRecipes becomes an array where each element conforms to the type { title: number, image: string }, which aligns with the defined Recipe type.

  1. Finally, you update the state without encountering any errors.
setRecipes(extractedRecipes);

Since useState<Recipe[]>([]) specifies the type as Recipe[] and you are supplying a similar structure to setRecipes, TypeScript recognizes the compatibility and no issues arise.

A Different Approach

In a subsequent example, there are some variations:

  1. The correct definition of Recipe and the introduction of a new type called RecipeResponse for the API response.
type Recipe = {
  title: string;
  image: string;
};

type RecipeResponse = {
  title: number;
  image: string;
};
  1. Now, the response comprises an array of objects conforming to RecipeResponse.
const response = await axios.get<RecipeResponse[]>(...
  1. The processing stage yields an array of elements following the pattern { title: number, image: string }, matching the RecipeResponse type.
const extractedRecipes = response.map((recipe) => ({
    title: recipe.title,
    image: recipe.image,
}));
  1. While the initial useState<Recipe[]>([]) defines the type as Recipe[], the usage of setState with RecipeResponse[] triggers an error detection by TypeScript.
setRecipes(extractedRecipes);

Final Thoughts

TypeScript acts as a validation tool, ensuring that your code adheres to the predefined rules and specifications.

In both instances discussed, TypeScript served its purpose by verifying adherence to constraints and flagging violations when encountered.

If the data modeling in the first scenario didn't accurately reflect reality, did TypeScript falter by not signaling an issue? Not necessarily. Its role is to validate constraints and enforce contracts – if everything aligns, TypeScript remains silent.

Conversely, in the second scenario, deviations from established restrictions prompted TypeScript to raise an alert.

Regarding Best Practices

In terms of "best practices," focusing on modeling data as realistically as possible takes precedence over arbitrary guidelines.

If the API responses mirror the objects intended for storage, employing the same type simplifies handling.

Alternatively, if certain API properties may contain null values undesired in storage, crafting a distinct type for the API response with nullable fields would be appropriate.

Given TypeScript's diverse set of types, utility types, and combination capabilities, developers can establish constraints that closely align with expected scenarios.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

ESLint encountered an issue: Reserved keyword 'interface' triggered a parsing error

Whenever I utilize the most recent version of eslint to initiate a project, a specific error pops up: import { ref } from 'vue' defineProps<{msg: string}>() const count = ref(0) Error message: Unexpected token )eslint Adjusting the code ...

What is the best approach for managing Create/Edit pages in Next.js - should I fetch the product data in ServerSideProps or directly in the component?

Currently, I am working on a form that allows users to create a product. This form is equipped with react-hook-form to efficiently manage all the inputs. I am considering reusing this form for the Edit page since it shares the same fields, but the data wil ...

What specific event do I require for the onChange event in React using TypeScript?

I'm facing a problem while working with React TypeScript. I need to type the onChange event for a select element, but the data is coming from event.value instead of event.target.value. What should be the appropriate event to use in this case? Below i ...

Ignore verification of unused parameters

In my typescript project compilation process, I make use of the noImplicitAny option to ensure that I specify the types for variables and arguments. However, there are instances where I have unused arguments. For instance: jQuery.ajaxTransport("+*", func ...

I am retrieving data from a service and passing it to a component using Angular and receiving '[object Object]'

Searching for assistance with the problem below regarding my model class. I've attempted various approaches using the .pipe.map() and importing {map} from rxjs/operators, but still encountering the error message [object Object] export class AppProfile ...

What is the recommended TypeScript type to be returned from a GraphQL resolver when using ESLint?

Repository Link https://github.com/inspiraller/apollo-typescript The code is functioning correctly, however, Eslint typescript is raising complaints. An eslint error occurs on the following code block: Query: { players: () => players } Miss ...

What are the steps to retrieve a Json Object from an Array while extracting information from Rapid API?

Utilizing axios to fetch data from a GET API on RapidAP returns an array of JSON objects, as illustrated in the images below. How can I implement Typescript within React to specifically extract the data of these JSON objects from the array according to my ...

What is the best way to access the "document" in a vscode webview provider?

I am currently working on developing an extension for vscode that utilizes React for the interface. However, I have encountered an issue where I am unable to insert my react root component into the HTML of the webview. Despite trying various approaches, n ...

determine function output based on input type

Here's a question that is somewhat similar to TypeScript function return type based on input parameter, but with a twist involving promises. The scenario is as follows: if the input is a string, then the method returns a PlaylistEntity, otherwise it ...

Utilizing TypeScript Variables within a Jquery Each Iteration

I have a variable named tableIndexNumber that I need to use in different methods. When trying to access this variable, I use "this.tableIndexNumber" and it works fine. However, I face an issue when using it inside a jQuery each loop because the HTML elemen ...

Adjusting the timeout for a particular operation according to its unique identifier

I am looking for a solution to call a method that posts an answer after an input change in my Angular project. I want to reset the timeout if another input change occurs to avoid multiple posts. Is there a smart way to achieve this? My project involves po ...

Is it possible to preserve the numerical type of a percentage when applying number formatting?

After applying number formatting, I converted a numerical value of 150 to 150.00%. Although this is the desired display format with the percentage sign included, the data type remains as string instead of number. Is there a method to convert it back to a ...

How can I center align my loader inside app-root in Angular2+?

I've successfully added a basic spinner to my <app-root> in the index.html file. This gives the appearance that something is happening behind the scenes while waiting for my app to fully load, rather than showing a blank white page. However, I& ...

Angular - Sharing data between components with response value

I am currently in the process of restructuring my project, focusing on establishing communication between unrelated components while also waiting for a return value from a function call. Imagine having component1 with function1() and component2 with funct ...

What is the process for upgrading TypeScript to the latest version?

Is there a way to upgrade TypeScript version for ASP.net MV5 project in Visual Studio 2015? I attempted searching through Nuget but couldn't locate it. There seems to be an issue with the razor intellisense (index.d.ts file) and I'm hoping that ...

When converting an object into a specific type, the setter of the target type is not invoked

Imagine a scenario with a class structured like this: class Individual { private _name: string; get Name(): string { return this._name; } set Name(name: string) { this._name = name; } } Upon invoking HttpClient.get<Individual>(), it retrieve ...

How can methods from another class be accessed in a TypeScript constructor?

I need to access a method from UserModel within the constructor of my UserLogic class. How can I achieve this? import { UserModel, ItUser } from '../../models/user.model'; export class UserLogic { public user: ItUser; constructor() { ...

Can someone explain how to showcase a collection attribute in Angular?

I am working with a collection of objects called "ELEMENT_DATA". Each object in the collection has an attribute named "Activite", which is also a collection. My goal is to display this attribute in a specific way. Below is my app.component.ts: export cla ...

Can JSON Web Tokens be utilized in a browser environment?

Searching for a way to parse the JWT token led me to this jsonwebtoken library https://www.npmjs.com/package/jsonwebtoken. It appears to be tailored for NodeJS. Is it feasible to utilize this library in the browser? My attempts so far have resulted in the ...

I would appreciate it if someone could explain the significance of the image display area

Please add a description for this image The table presented below outlines different declaration types: Declaration Type Namespace Type Value Namespace X X Class X X Enum X X Interface X Type Alias X Function X Variable X A ...