-4

I am trying to declare global prototype function in Next.js, React, Typescript, but I am getting error <object>.<functionname> is not a function.

I have a file ArrayFunc.ts, in which I have:

export {}

declare global {
    interface Array<T> {
        distinct(): T[]
    }
}

Array.prototype.distinct = function (): Array<any> {
  return Array.from(new Set<T>(array))
}

export function importArrayFunctions () {}

then I have Page:

'use client'

export const MyPage: FC<{data: number[]}> = ({ data }) => {
  return <div>{data.distinct().length}</div> -- error here
}

If I have only this, it does not work.

I have to call importArrayFunctions() at beginning to make it work in every Page file but there is a lot of files and lot of prototype functions.

Is it possible to do it better way? Or how to declare prototype function correctly so server side and client side can see it?

5
  • 3
    "...and lot of prototype functions" don't muck around with prototypes you don't own. You also run the very real risk of your stuff breaking in the future if they ever add methods with the same name to Javascript with different behavior. The short answer to "how to do this" is "don't do that": that's been a JS antipattern for over a decade now. There's no good reason: it's easier and safer to just write a plain utility function that takes an array as an argument and does the logic. Commented Sep 29 at 11:02
  • Also keep in mind that importing something just for the types will usually make TypeScript not transpile that import in your emitted JS files which will break your code (which you may observe as a "I need to actually call a function exported from this file to get it to work" kind of situation). You can get around this by using the verbatimModuleSyntax option but you can already see how much messier this is getting than to simply write a function that takes an array->use the function that takes the array solution Commented Sep 29 at 14:52
  • I am using extension functions (prototype functions) in C# since 2007 and in Kotlin since 2017 and never had a problem, quite opposite. I know that JS world is more fragile and code is not glued together so well, but in projects which I have under control this functionality is not the issue and it makes code more readable and short. So if there is a way how to do it I will appreciate advice. If not, then, well, thanx anyway and I will use the old way. Thank you. Commented Sep 29 at 16:21
  • 1
    "I have to call importArrayFunctions() at begining to make it work" - no, but you have to at least import the module that does the prototype modifications; otherwise that code won't run. You haven't shown that part of your page file. Commented Sep 29 at 20:36
  • 1
    "I am using extension functions (prototype functions) in C# since 2007 and in Kotlin since 2017" in C# you aren't (typically) shipping your code off for someone else to run. You have control over the environment, if an upgrade to the CLR breaks stuff you'll know before your users. I'm also pretty sure (not a C# dev) that those require importing a namespace explicitly and don't just trample on global state for everybody? (less sure about that part). Kotlin it depends whether we're talking server or Android, but still in JS, at least for browsers, you have zero control over the environment Commented Oct 3 at 11:54

2 Answers 2

0

You don't, you declare a function and pass the array in as a parameter. Modifying the prototype is not recommended.

export const distinct = (arr) =>
    arr.filter((item, index) => !arr.some((item2, index2) => item === item2 && index > index2));
Sign up to request clarification or add additional context in comments.

Comments

0

First, as mentioned in the comments (right under the question), in fragile JavaScript world is better to use long format like module function or static class.

Static class example:

class ArrayUtils {
  static distinct<T>(array: T[]): T[] {
    return Array.from(new Set<T>(array))
  }
}

export default ArrayUtils

usage

const resultArray = ArrayUtils.distinct(sourceArray)

But if you know what you are doing or you prefer readability of the code then the solution is:

  1. Separate Type Declarations (src/types/array.d.ts)
declare global {
  interface Array<T> {
    distinct(): T[]
  }
}
  1. Extension implementation (src/utils/array-extensions.ts)
Array.prototype.distinct = function <T>(): T[] {
  return Array.from(new Set<T>(this))
}
  1. Client Wrapper (app/ClientWrapper.tsx)
'use client'

import '@/utils/array-extensions' // Import here for client-side
import { FC, ReactNode } from 'react'

const ClientWrapper: FC<{ children: ReactNode }> = ({ children }) => {
  return <>{children}</>
}

export default ClientWrapper
  1. Layout (app/layout.tsx)
import ClientWrapper from './ClientWrapper'
import '@/utils/array-extensions'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <ClientWrapper>{children}</ClientWrapper>
      </body>
    </html>
  )
}
  1. Usage
'use client'

import { FC } from 'react'

export const MyPage: FC<{ data: number[] }> = ({ data }) => {
  return <div>{data.distinct().length}</div>
}

2 Comments

"in JavaScript it is better to use static class" - no, don't do that. Use class syntax only for things you actually want to instantiate.
"But if you know what you are doing then if (!Array.prototype.distinct) { …" - no, drop that if statement. This is exactly what will make your code break when some new browser adds a native distinct method that does something different than your implementation (or worse, it will prevent the web from advancing, because browser vendors are afraid to break your site; see SmooshGate). So if you really knew what you were doing, you would work under the assumption that all code on the page is yours, and just overwrite Array.prototype.distinct

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.