What is the correct syntax for using the apply method in combination with console.log()?

Below is the class I've created to capture the console.log function in my application. This allows me to review logs on devices where accessing the browser console isn't easy, and also helps in bundling captured logs for error reporting later on.

To make it work, I had to include ts-ignore as shown below:

// @ts-ignore
oldDebug.apply(console, arguments);

If I remove the ts-ignore directive, I encounter the following error:

Argument of type 'IArguments' is not assignable to parameter of type '[any?, ...any[]]'. 

Here's the code snippet used for capturing the logs:

let oldLog: typeof console.log;

export type LogLevel = "log"|"debug"|"warn"|"error";

export interface ConsoleLogMessage {
  level: LogLevel,
  arguments: IArguments,
}

export function interceptConsoleLogs() {
  if (oldLog) {
    throw new Error("Log functions already intercepted");
  }

  oldLog = console.log;
  window.console.log = function() {
    storeLogMessage("log", arguments);
    // @ts-ignore
    oldLog.apply(console, arguments);
  };

  // debug, warn, error...

}

export function getCurrentLogMessages(): ConsoleLogMessage[] {
  return logStorage.slice(0);
}

const maxLogs = 100;
const logStorage: ConsoleLogMessage[] = [];

function storeLogMessage(level: LogLevel, args: IArguments) {
  if (logStorage.length >= maxLogs) {
    logStorage.shift();
  }
  logStorage.push({level: level, arguments: args});
}

How should the types be structured to perform these apply calls without needing to add ts-ignore?


Environment: My application is a create-react-app project upgraded to TypeScript 3.6.3, with the following tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": "src",
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve"
  },
  "include": [
    "src"
  ]
}

Answer №1

The issue at hand lies in the fact that arguments is not truly an array.

It functions as an "array-like" object, which is explained by MDN in the following way:

"Array-like" indicates that arguments possesses a length property and properties indexed from zero, but it lacks the built-in methods of Array such as forEach() and map().


When operating in strict mode, the compiler performs rigorous type checking on the parameters of apply, revealing a mismatch with the nature of arguments due to its non-array characteristics.

As you have already discovered, utilizing rest parameters provides the optimal solution. This approach is favored over relying on arguments.

Additionally, this method simplifies the task of explicitly defining certain parameters while gathering the remaining values into an array:

window.console.log = function(message?: any, ...args: any[]){
  storeLogMessage({level: "log", message: message, args: args});
  oldLog.apply(console, [message, ...args]);
};

It's worth noting the alternative use of .call instead of

.apply</code, allowing for the direct passing of rest parameters without prior array construction:</p>

<pre class="lang-js"><code>window.console.log = function(message?: any, ...args: any[]){
  storeLogMessage({level: "log", message: message, args: args});
  oldLog.call(console, message, ...args);
};

The stringent validation applied to apply, call, and bind can be adjusted using the compiler option strictBindCallApply. It becomes active when strict: true is set. Furthermore, this explains why no errors surfaced on the typescriptlang.org/play platform. While some strict options are enabled on the playground, strictBindCallApply remains disabled.

Answer №2

This is the final code solution that successfully functions without requiring the use of the ts-ignore annotation.

window.console.log = function(message?: any, ...args: any[]){
  storeLogMessage({level: "log", message: message, args: args});
  oldLog.apply(console, [message, ...args]);
};

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

Typescript: The function parameter in a class method is more specific than in the superclass

I am facing an issue with two TypeScript classes where one extends the other: type NamedObject = { name: string; } class AnyObjectManager { objectList = []; getAnyObject = (matches: (o: object) => boolean) => { for (const o of t ...

Hierarchy-based state forwarding within React components

As I embark on the journey of learning Typescript+React in a professional environment, transitioning from working with technologies like CoffeeScript, Backbone, and Marionettejs, a question arises regarding the best approach to managing hierarchical views ...

Navigating through React with Typescript often involves managing the process of waiting for an API call to finish

My interface structure is as follows: export interface Chapter{ id: string, code: string } Within a component, I am making an API call in the following manner: componentDidMount() { fetch("https://someapi/getchapter") .then(r ...

Using the spread operator for type checking of generics is overly broad

While experimenting with interface inheritance and generics, I came across a peculiar behavior that might lead to runtime problems. This issue is observed in the latest release of TypeScript, version 5.0.3. Essentially, it seems that a function accepting a ...

Tips for defining the type restriction in the code provided

interface IRouteProps { path: string name: string } const routesConfig: IRouteProps[] = [ { path: '/login', name: 'login' } ]; let routeNames: any; const routes: IRouteProps[] = routesConfig.forEach((route: IRouteProp ...

"Sequencing http.get requests in Angular 2 using

In my service, I have a series of http.get requests structured as follows: constructor(private http:Http) {} getDetails(sysID:string){ var details; this.http.get('https://blahURL').map(res => res.json().filter(f => f.id == another.id)[0] ...

Tips for deactivating a specific value or graying it out within the list of value items in a drop-down menu using <ng-selectize> or <options> in Angular 7

I am facing a situation where I have to dynamically generate a list of options for a dropdown menu in my Angular 7 application. However, I need to be able to disable specific options in the list based on certain conditions or flags. For example: I am disp ...

What is the best approach to managing exceptions consistently across all Angular 2/ Typescript observables?

Throughout the learning process of Angular2, I have noticed that exceptions are often caught right at the point of the call. For example: getHeroes(): Promise<Hero[]> { return this.http.get(this.heroesUrl) .toPromise() ...

Filter multiple columns in an Angular custom table with a unique filterPredicate

Looking to develop a versatile table that accepts tableColumns and dataSource as @Input(). I want the ability to add custom filtering for each table column. Currently, I've set up the initialization of the table FormGroup and retrieving its value for ...

"Encountering the 'GetUserByAccountError' issue with Nextjs version 13.4, next-auth version 4.22.1, and @next-auth/prisma-adapter version 1.0.6

What triggers the error GetUserByAccountError when attempting to log in via SocialLogin with Github or Google? Here is a detailed description of the issue: Unknown arg `provider_providerAccountId` in where.provider_providerAccountId for type AccountWhereUn ...

The Angular Firebase query is being run repeatedly

I'm currently facing an issue in my project where Firebase queries are being executed multiple times. This problem wasn't present during development and no changes have been made to the Firebase dependencies. Below is a snippet of code that used ...

sticky header on pinned tables in a React data grid

I have combined 3 tables together, with the middle table containing a minimum of 15 columns. This setup allows users to horizontally scroll through the additional columns conveniently. However, I am facing a challenge in implementing a sticky header featu ...

The VSCode's intellisense for Angular Material fails to function effectively

In the midst of my project on Angular version 13, I have successfully installed Angular Material using the command below: ng add @angular/material The package has been properly included in the node_modules folder. However, when working with TypeScript ...

Tips on creating a hierarchical ul list from a one-dimensional array of objects

I have an array filled with various objects: const data = [ {id: "0"},{id: "1"},{id: "2"},{id: "00"},{id: "01"},{id: "02"},{id: "11"},{id: "20"},{id: "23"},{id: & ...

Stop Mat-chip from automatically inserting a row upon selection

I am working on preventing the automatic addition of a row by the mat-chip module after a single chip has been selected. Even though the max chip count is set to 1, the input remains enabled and adds a new row beneath it as if the user can still type more ...

Error: Attempting to access 'userService' property of undefined object without any value

I encountered an issue with the error message: TypeError: Cannot read properties of undefined (reading 'userService') This occurred while attempting to access a method from my UserService class in another class named ServicecenterController. In ...

What is the process for verifying the existence of a key value pair within one array in comparison to another array?

I have two different sets of data, represented by arrays: invoices: [ {id: 90, Client: 'Bob', paid: false, total: 900}, {id: 91, Client: 'Sarah', paid: false, total: 400} ] and: result: [{km: 200, hours: 20, Person: 'Sa ...

What are some ways to streamline inline styling without having to create numerous variables?

Currently, I am in the process of constructing a tab component and establishing inline variables for CSS styling. This particular project involves a streamlit app that allows me to modify settings on the Python side. At the moment, there are four elements ...

Using React's useState hook with an empty array

interface Crumb { title: string; url: string; } interface Crumbies { crumbsArray: Crumb[]; } // component const [breadcrumbs, setBreadcrumbs] = useState<Crumbies>([]); I encountered an issue: TS2345: Argument of type 'never[]' is ...

Angular: failure to update a specific portion of the view

I'm currently working on a directive template that features the following code snippet: <div class="colorpicker"> <div>Chosen color</div> <div class="color_swatch" style="background-color: {{ngModel}}">&nbsp;</div> & ...