Search code examples
typescripttyping

How to disable explicit typing for a certain interface


I have an error renderer function in my project. It creates two functions

res.errorJson // Will only send a JSON body to the client
res.error // Will send an HTML error if the client is a browser. Otherwise we'll send json

Before you ask, yes I made sure to add types for these two functions in express.d.ts

res.error takes two parameters, first one is the status code, and the second one is the parameters we want to send in the response.

This is my interface for errorOptions

// res.error(statuscode: number, options: errorOptions)
declare interface errorOptions {
  title?: string;
  description?: string | Array<string>;
  customHTML?: string;
  jsonOnly?: boolean
}

Lets say call res.error with an additional option thats not inside the interface, typescript will yell at me

I want to disable explicit typing for this ONE interface.

Basically just so I have the pretyped types in the interface, and if I want to add something else to the options, I can do so without typescript yelling at me. I hope this makes sense and I hope I'm not dumb

Here is all my code for my error renderer

res.errorJson = (statusCode: RangeOf2<100,599>, options?: errorOptions) => {
    options.jsonOnly = true;
    return res.error(statusCode, options);
  };
  res.error = (statusCode: RangeOf2<100,599>, options?: errorOptions) => {
    if (typeof(statusCode) !== "number") {
      throw new Error("Status code is not a number");
    }

    options = options || {};
    
    if (Object.prototype.hasOwnProperty.call(errors, statusCode)) {
      options.title = errors[statusCode];
    } else {
      options.title = options.title || `${statusCode} Not defined`; 
    }

    if (req.useragent.isBot !== false || options.jsonOnly === true) {
      
      let data = {
        success: false
      };

      for (const v in data) {
        console.log(v);
      }

      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore ignore this until espi gets back to me
      data.success = false;

      res.send(JSON.stringify(data, null, 2) + "\n");
      return data;
    }
    ejs.renderFile(path.join(__dirname, "errorTemplate.ejs"), {data: options}).then(render => {
      try {
        res.status(statusCode).send(render);
      } catch (error) {
        console.error("You are sending an error after headers were sent to the client.");
        console.log(error);
      }
    });
    return options;
  };
  next();

Solution

  • For this, you should be able to declare your interface to accept arbitrary parameters:

    
    declare interface errorOptions {
      title?: string;
      description?: string | Array<string>;
      customHTML?: string;
      jsonOnly?: boolean;
      [x: string]: any;
    }
    
    

    See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#strict-object-literal-assignment-checking

    "If the target type accepts additional properties, add an indexer"