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;
}
