Is there a way to silence TypeScript's complaints about a React rendered value being converted into a Date object?

In my latest project using Next.js, TypeScript, Prisma, and Radix-UI, I'm working with a loan object that has various key-value pairs:

{
    "id": 25,
    "borrowerName": "Test Borrower 1",
    "pipelineStage": "PROSPECT",
    "loanAmount": 500000,
    "transactionType": "PURCHASE",
    "referralSource": "Realtor Lenny",
    "borrowerEmail": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="fd89988e89ccbd9a909c9491d39e9290">[email protected]</a>",
    "propertyAddress": "1234 Test Way",
    "borrowerPhone": "789-546-3142",
    "purchasePrice": 700000,
    "creditScore": 700,
    "createdAt": "2024-03-15T21:46:18.347Z",
    "updatedAt": "2024-03-15T21:46:18.347Z"
}

One of the challenges I encountered was when building a "view single loan" page while trying to convert the createdAt and updatedAt strings from ISO format into a more user-friendly format. Here's a snippet of the code where I loop over the keys and values in one go:

{Object.keys(loan || {}).map((loanKey) => {
                if (loanKey === "id") return null;

                if (loan && loanKey in loan) {
                  let value: string | number | Date | null =
                    loan[loanKey as keyof typeof loan];

                  if (loanKey === "createdAt" || loanKey === "updatedAt") {
                    value = new Date(value!).toLocaleString(undefined, {
                      year: "numeric",
                      month: "long",
                      day: "numeric",
                      hour: "numeric",
                      minute: "numeric",
                    });
                  }
                  return (
                    <Card className="text-black">
                      <Flex direction={"column"} align={"center"}>
                        <Text>{formatKeyDisplay(loanKey)}: </Text>
                        <Card>{value}</Card>
                      </Flex>
                    </Card>
                  );
                }
                return null;
              })}

Even though I attempted to cast the value to a Date object and then back to a string for rendering purposes, TypeScript still raised an error stating:

Type 'string | Date | null' is not assignable to type 'ReactNode'.
  Type 'Date' is not assignable to type 'ReactNode'.ts(2322)

I also tried typing the value differently with

let value: string | number | null = loan[loanKey as keyof typeof loan];
but it led to another TypeScript error:

Type 'string | number | Date | null' is not assignable to type 'string | number | null'.
  Type 'Date' is not assignable to type 'string | number | null'.ts(2322)

Answer №1

For better organization, it is recommended to use separate variables for the unprocessed value and the processed value that you can guarantee will not be a Date:

let rawValue: string | number | Date | null =
  loan[loanKey as keyof typeof loan];
let value: string | number | null;

if (loanKey === "createdAt" || loanKey === "updatedAt") {
  value = new Date(rawValue!).toLocaleString(undefined, {
    year: "numeric",
    month: "long",
    day: "numeric",
    hour: "numeric",
    minute: "numeric",
  });
} else if (rawValue instanceof Date) {
  // Throw error, as only specific keys should contain dates
  throw new Error(`Unexpected Date for field "${loanKey}"`);
} else {
  // This assignment should be safe after filtering out Date types above
  value = rawValue;
}

Playground link

Answer №2

To show a different message on the screen, create a new variable called displayValue and assign it the desired string instead of overwriting the existing variable value. This approach helps Typescript maintain proper type consistency without any confusion.

Answer №3

I attempted to replicate the issue you mentioned on a test scenario, but I did not encounter any errors.

import React from 'react';

const displayFormattedKey = (key: string) => {
    return key.toString();
};

export default function App() {
    const sampleData = {
        id: 25,
        borrowerName: "Test Borrower 1",
        pipelineStage: "PROSPECT",
        loanAmount: 500000,
        transactionType: "PURCHASE",
        referralSource: "Realtor Lenny",
        borrowerEmail: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f98d9c8a8dc8b99e94989095d79a9694">[email protected]</a>",
        propertyAddress: "1234 Test Way",
        borrowerPhone: "789-546-3142",
        purchasePrice: 700000,
        creditScore: 700,
        createdAt: "2024-03-15T21:46:18.347Z",
        updatedAt: "2024-03-15T21:46:18.347Z"
    };

    return (
        <div style={{backgroundColor: "white", width: "100%", minWidth: "500px"}}>
        {Object.keys(sampleData || {}).map((dataKey) => {
            if (dataKey === "id") return null;

            if (sampleData && dataKey in sampleData) {
                let value: string | number | Date | null =
                    sampleData[dataKey as keyof typeof sampleData];

                if (dataKey === "createdAt" || dataKey === "updatedAt") {
                    value = new Date(value!).toLocaleString(undefined, {
                        year: "numeric",
                        month: "long",
                        day: "numeric",
                        hour: "numeric",
                        minute: "numeric",
                    });
                }
                return (
                    <div className="text-black" key={dataKey}>
                    <div style={{display: "flex", gap: "15px", direction: "row"}}>
                        <div>{displayFormattedKey(dataKey)}: </div>
                        <div>{value}</div>
                    </div>
                    </div>
                );
            }
            return null;
        })}
        </div>
    );
}

Is it possible for you to create a Component demo so we can correctly identify the issue?

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

Steps to replenish the firebase cacheHere's how you

I'm encountering an issue with my Next.js project, specifically a Next.js website hosted on Google Firebase. I am utilizing Google Cloud Functions to run this website. I am interested in implementing cache for better performance. It is my understandi ...

Fetching data from the server in NextJS based on user input

After assembling a Client and a Server component (using App Router), I was able to refresh the Server when the user interacts with it, triggering a route refresh by using: router.push(pathname) Although that worked well for refreshing the Server, now I ne ...

Error encountered: Parsing error in Typescript eslint - The use of the keyword 'import' is restricted

My CDK application is written in typescript. Running npm run eslint locally shows no errors. However, when the same command is executed in a GitLab pipeline, I encounter the following error: 1:1 error Parsing error: The keyword 'import' is r ...

Type guards do not work properly on a union of enum types in TypeScript

Recently delved into the concept of Type Guards Chapter within the realm of Typescript However, I encountered an issue where my basic type guards failed to differentiate a union of enums. Why is this happening? enum A { COMMA = ',', PLUS = & ...

Remap Objects Function with Correct Return Data Type

After receiving data from another source via a post request in a large object, I need to extract specific fields and organize them into more precise objects with some fields remapped before inserting them into a database. Currently, I have a working solut ...

Frustratingly Quiet S3 Upload Failures in Live Environment

Having trouble debugging a NextJS API that is functioning in development (via localhost) but encountering silent failures in production. The two console.log statements below are not producing any output, leading me to suspect that the textToSpeech call ma ...

Implementing Service Communication

I created an Angular Application using the Visual Studio Template. The structure of the application is as follows: /Clientapp ./app/app.module.shared.ts ./app/app.module.client.ts ./app/app.module.server.ts ./components/* ./services/person-data.service. ...

When using Angular version 13 alongside rxjs 7.4 and TypeScript 4+, an issue arises with the error message: "Declaration file for module 'rxjs' not found"

Currently embarking on a new Angular app using V13 and rxjs7.4, I encountered the error shown above when trying to import: import { BehaviorSubject } from 'rxjs'; Initially, I attempted to address this by creating a typings.d.ts declaration as s ...

Generate a collection of elements using a different collection as a reference

I am struggling with an array of objects: let data = [{ createdDate: "2222", email: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="087c6d7b7c3d487c6d7b7c266b6765">[email protected]</a>", histories: [ ...

What could be the reason behind the error related to react-router-dom?

index.tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.S ...

Ways to keep information hidden from users until they actively search for it

Currently, I have a custom filter search box that is functioning correctly. However, I want to modify it so that the data is hidden from the user until they perform a search. Can you provide any suggestions on how to achieve this? Below is the code I am u ...

Allowing scroll events to flow through from a child <div> to <canvas> in Excalidraw

In my current project, I am utilizing Excalidraw to draw a layer of <div>s on top of the <canvas>. While everything is functioning well, I have encountered an issue with scrolling through the infinite canvas. When the mouse hovers over one of t ...

When utilizing the file-loader in Webpack with a function in the outputPath parameter, an EISDIR error occurs,

I am attempting to create a specific output format for my locale files in the form of _locales/[locale_shortcut]/[file].json To achieve this, I am utilizing the file-loader plugin within webpack. While the documentation mentions the use of a function with ...

Upon attempting to utilize Socket.io with Next.JS custom server, the server repeatedly restarts due to the error message "address already in

Every time I attempt to execute the refreshStock() function within an API endpoint /api/seller/deactivate, I encounter the following error: Error: listen EADDRINUSE: address already in use :::3000 at Server.setupListenHandle [as _listen2] (net.js:1318: ...

Encountering an issue with Next Start - Unable to locate index.js file and receiving an error stating module cannot

I initially started my Next.js project a while ago with the command npx create-next-app --typescript. Current Next version: 10.0.4 In my package.json file: "scripts": { "dev": "next dev", "build": "n ...

Exploring the not-found.tsx page within the framework of multiple root layout.jsx components in Next JS

Seeking some assistance here. I'm organizing the structure of my NextJS 14 app like this: app/ (admin)/ layout.tsx (web)/ layout.tsx page.tsx not-found.tsx Just a heads up, there's no layout.tsx page at the ...

Combinations of Typescript dependent unions

I'm struggling with calling the given union that wraps a function and its argument. Is there a way to call it without having to cast? type Wrapper = { fn: (a: string) => void arg: string } | { fn: (a: number) => void arg: number } let f ...

Invoke the method in customButton component of fullcalendar

I've integrated a custom button into my fullcalendar: ngOnInit() { this.calendarOptions = { customButtons: { custom1: { text: 'Add event', click() { this.openModal(); } } }, height: 600, editable: t ...

Using Angular 5+ to fetch information from an ASP.NET Core Web API

Having trouble retrieving data from an ASP.NET Core 2.0 Web API with Angular 5+. The steps taken so far are as follows: An ASP.NET Core 2.0 Web API was created and deployed on a server. Data can be successfully retrieved using Postman or Swagger. Using ...

Component coding in Angular 2 allows for seamless integration and customization of Material

I am looking to initiate the start.toggle() function (associated with Angular 2 material md-sidenav-layout component) when the test() method is triggered. How can I execute md-sidenav-layout's start.toggle() in the app.component.ts file? app.componen ...