Home > Mobile >  Extending Express Response.render() type definition with TypeScript
Extending Express Response.render() type definition with TypeScript

Time:10-23

I'm attempting to get some more intelligent typing in my Express project but am having trouble extending the Response.render function.

import { Response } from "express";
import { Product } from "../models/Product.interface";

export interface ProductListResponse extends Response {
    render: (view: string, options?: { products: Product[] }) => void;
}

The origin definition looks like:

render(view: string, options?: object, callback?: (err: Error, html: string) => void): void;
render(view: string, callback?: (err: Error, html: string) => void): void;

When I try to compile this I get

src/routes/product.interface.ts:13:18 - error TS2430: Interface 'ProductListResponse' incorrectly extends interface 'Response<any, Record<string, any>>'.
  Types of property 'render' are incompatible.
    Type '(view: string, options: { products: Product[]; }) => void' is not assignable to type '{ (view: string, options?: object, callback?: (err: Error, html: string) => void): void; (view: string, callback?: (err: Error, html: string) => void): void; }'.
      Types of parameters 'options' and 'callback' are incompatible.
        Property 'products' is missing in type '(err: Error, html: string) => void' but required in type '{ products: Product[]; }'.

13 export interface ProductListResponse extends Response {
                    ~~~~~~~~~~~~~~~~~~~

  src/routes/product.interface.ts:14:37
    14     render(view: string, options: { products: Product[] }): void;
                                           ~~~~~~~~
    'products' is declared here.


Found 2 errors.

I thought I could just overwrite the definition of render by extending the base interface but apparently that isn't true. Any help would be greatly appreciated. Thanks!

CodePudding user response:

You need to add the .render() method overload signatures of the Response interface to your custom ProductListResponse interface.

import express, { Response } from 'express';

interface Product {}

export interface ProductListResponse extends Response {
  render(view: string, options?: { products: Product[] }): void;
  render(view: string, options?: object, callback?: (err: Error, html: string) => void): void;
  render(view: string, callback?: (err: Error, html: string) => void): void;
}

const app = express();

app.get('/', (req, res: ProductListResponse) => {
  res.render('index', { products: [] });
});

Or, use Intersection Types

import express, { Response } from 'express';

interface Product {}

export interface ProductListResponse {
  render(view: string, options?: { products: Product[] }): void;
}

const app = express();

app.get('/', (req, res: ProductListResponse & Response) => {
  res.render('index', { products: [] });
});

TypeScript version: 4.4.4

CodePudding user response:

You can omit render definition in the express's Response type. Then, redefine your render function:

interface ProductListResponse extends Omit<Response, 'render'> {
  render: (view: string, options?: { products: Product[] }) => void;
}
  • Related