Exploring the implementation of custom global declaration in the latest version of NextJS, version

Looking to implement custom global declaration in NextJS

In my NextJS project, I've defined a global prototype for String as shown below

utils.d.ts

export {}

declare global {
  interface String {
    /**
     * Returns string after removing all html tags.
     */
    stripHtml(): string
  }
}
String.prototype.stripHtml = function (): string {
  return this.replace(/(<([^>]+)>)/gi, '')
}

Then I've added utils.d.ts file to tsconfig.json like so

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "paths": {
      "@/*": ["./*"]
    },
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "include": ["utils.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

Next, I've utilized it within my component Example.tsx like below

Example.tsx

import React from 'react'

interface Props {
  posts: Record<string, any>[]
}

const Example: React.FC<Props> = ({ posts }) => {
  return (
    <section>
      <div className="container">
        <div>
          {posts.map((item, idx) => {
            const post = item.attributes
            const { title, content } = post
            return (
              <div key={`post-${idx}`}>
                <div>
                  <h2>{title}</h2>
                  <p>{content.stripHtml()}</p>
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </section>
  )
}

export default Example

Now the VS Code no longer shows errors related to the declaration. However, when I run the project using the dev command, I encounter the following error.

TypeError: content.stripHtml is not a function
    at eval (webpack-internal:///./components/Example.tsx:37:58)
    ...
    (additional error messages)

Answer №1

You followed all the TypeScript guidelines correctly, but the issue arose because you forgot to extend the String prototype with the stripHtml function. This resulted in the error message: "stripHtml is not a function."

To fix this, it's recommended to declare the type in a utils.d.ts file and extend the String prototype in a separate utils.ts file. Then, import it at the top-level of your application, ideally in the root layout.tsx file to make it accessible throughout the components tree.

For example:

File: utils.ts

import './utils.d'

String.prototype.stripHtml = function (): string {
    return this.replace(/(<([^>]+)>)/gi, '')
}

File: utils.d.ts

export {}

declare global {
    interface String {
        /**
         * Returns string after removing all html tags.
         */
        stripHtml(): string
    }
}

File: app/layout.tsx

import '@/utils'

Edit: P.s. This approach is commonly referred to as prototype pollution, which is considered bad practice. It's advisable to declare utility functions in modules and only import them where necessary.

Answer №2

Here are some helpful tips that might solve your issue:

  1. Double check that the tsconfig.json is correctly used by the TypeScript compiler.
  2. Inspect the generated JavaScript files after building to confirm that stripHtml is present in String.prototype.
  3. Verify the import order, ensuring that the utils.d.ts file is imported before any other files containing custom declarations.
  4. Don't forget the simplest solution: have you tried restarting the Next.js development server?

If you're still facing trouble, would you mind sharing your webpack/babel configurations for further analysis?

Answer №3

Ensure that when using stripHtml in your component, the file utils.d.ts is included in the build output and imported beforehand.

There are other methods you may want to consider:

Utility Function: If making global changes is not necessary, think about creating a utility function instead of extending String.prototype:

function stripHtml(str: string): string {
  return str.replace(/(<([^>]+)>)/gi, '');
}

Custom Hook: For reusable logic, develop a custom hook:

import { useEffect } from 'react';

function useStripHtml() {
  useEffect(() => {
    String.prototype.stripHtml = function () {
      return this.replace(/(<([^>]+)>)/gi, '');
    };
  }, []);
}

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

What is the best approach to organize data from an observable based on a nested key?

I'm currently developing a new application and struggling with grouping data. The data is being pulled from an observable, and I need to group objects by their status and push them into an array. I attempted to use the groupBy() method, but unfortunat ...

The JestImportMeta interface is mistakenly extending the ImportMeta interface, causing an error

While transitioning from jest version 27 to v29, I encountered this issue: node_modules/@jest/environment/build/index.d.ts:329:26 - error TS2430: Interface 'JestImportMeta' improperly extends interface 'ImportMeta'. The types returned ...

You were supposed to provide 2 arguments, but you only gave 1.ts(2554)

Hey everyone, I hope you're having a good morning. Apologies for the inconvenience, I've been practicing to improve my skills and encountered an issue while working on a login feature. I'm trying to connect it to an API but facing a strange ...

What is the best way to ensure the secure signing of a transaction in my Solana decentralized application (

I am currently involved in an NFT project that recently experienced a security breach, and I am developing a dapp to rectify the situation. Our plan is to eliminate all NFTs from the compromised collection and issue a new set of NFTs using our updated auth ...

Using the useSWR hook to fetch data with filtering query parameters

A project I'm working on involves a component that displays a list of sources. Users have the ability to filter this list using search keywords. In order to store the input value, I created a state called searchKeyword. When a user clicks on the searc ...

Typescript: Securing Data with the Crypto Module

I am currently working on encrypting a password using the built-in crypto module. Previously, I used createCipher which is now deprecated. I am wondering if there is still an effective way to achieve this. Here is the old code snippet: hashPassword(pass: ...

Angular 6: A class with no 'default' modifier must explicitly specify a name

I am encountering this error in my ts.file, as I delve into the world of Angular/Ionic. Can anyone help identify the possible reasons for this? I have attempted multiple solutions to address it, but unfortunately, none have proven successful. import { Co ...

The canvas is currently being used. Prior to reusing the canvas, Chart ID '0' must be destroyed. -chart.JS

I have the necessary data for the chart, but I am encountering a warning that I need to resolve without causing any damage to the existing chart. I tried using myChart(update) to update the date on the chart, however, this method is not working. Is there ...

Issue: Encounter of "Uncaught (in promise) TypeError" while implementing RiveScript chatbot in Angular

I've been working on integrating a chatbot based on RiveScript into Angular. The chatbot is functioning well - I always see the correct response in the console. Displaying the user's input is also working seamlessly. However, I'm encounterin ...

Deploying Next.js on an AWS EC2 (Linux instance) Tutorial

I have successfully developed a Next.js application. My current challenge involves deploying this application on an AWS EC2 instance. I have managed to deploy it on the EC2 instance, similar to how it runs on my local development server by moving the enti ...

Seeking assistance with managing Yarn workspaces

My current project involves working on a React Native application for a client. After I had already written a significant amount of code, my client requested a new feature to be added. This feature should have been simple to implement, but due to the compl ...

@tanstack/react-query: Cannot find a QueryClient, please provide a QueryClientProvider to set one

Issue Statement: The error I am encountering only occurs in production (next.js) and not consistently, but rather sporadically. Within my next.js project, I am utilizing the package @tanstack/react-query. Occasionally, in production, without warning, I en ...

What is the best way to send multiple data using GetServerSideProps?

I have a challenge where I need to pass multiple sets of sanity data into a component, but I am restricted to using getServerSideProps only once. How can I work around this limitation to include more than one set of sanity data? pages > members.tsx exp ...

Uploading Boolean Values from Switch Input (React/Typescript)

I'm facing an issue while trying to post the state value of a switch input toggle control. Whenever I use the submitRecommendation() function through a button click, I encounter a JSON parse error: Cannot deserialize instance of `boolean` out of START ...

Crafting a nested path type in Typescript

Currently, I am working on developing a recursive type in TypeScript to iterate through every potential route path, even nested paths, from a provided route configuration. However, I have hit a roadblock with type constraints and inference. The routes are ...

Unable to activate IndexedDb persistence with Firebase v9 in a Next.js PWA

I'm having trouble enabling IndexedDb persistence in Firebase v9 for a Next.js PWA. These errors keep popping up: index.js // main Firebase file import { initializeApp } from 'firebase/app' import { getAuth } from 'firebase/auth' ...

Closures are like the "use" keyword in PHP or the capture list in C++, but they play a similar role in JavaScript and other transpiler languages

PHP has a useful feature with the use keyword, which allows for the usage of 'external' variables in closures. For example: $tax = 10; $totalPrice = function ($quantity, $price) use ($tax){ //mandatory 'use' return ($price * $quan ...

Discovering the World of React with Typescript: Implementing Flexible Routes with BrowserRouter

When navigating to http://localhost:3000/confirm_email/, the route loads correctly. However, if I navigate to http://localhost:3000/confirm_email/h8s03kdbx73itls874yfhd where h8s03kdbx73itls874yfhd is unique for each user, I still want to load the /confirm ...

What is the process for adding a Background Image in Nextjs?

In my current project, I am utilizing Nextjs and Material-UI. However, I have encountered an issue when trying to set a background image compared to previous projects using React. As this is my first time working with Nextjs, I attempted importing Image ...

Have you ever wondered why the React HeroIcons architecture includes React.createElement instead of simply returning plain SVG elements?

As I integrate HeroIcons into my Next.Js app, I find myself pondering over how they have structured their package architecture. The way they return icons is like this: const React = require("react"); function ArchiveIcon(props, svgRef) { retur ...