Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overwrite `any` in TypeScript when merging interfaces

I'm using Express and I'm trying to explicitly define res.locals. In the @types/express package, Express.Response.locals is any, so I can't seem to overwrite it:

types/express/index.d.ts:

declare namespace Express {
  interface Response {
    locals: {
      myVar: number
    }
  }
}

My Middleware:

import * as express from 'express'

function middleware(
  req: express.Request, 
  res: express.Response, 
  next: express.nextFunction
) {
  res.locals.myVar = '10' // I want this to throw a compiler error
  next()
}

I want my wrong assignment of res.locals.myVar to error, but res.locals is still any according to my autocompletion.

How can I remove any and completely replace it?

like image 564
Daniel Diekmeier Avatar asked Mar 26 '19 17:03

Daniel Diekmeier


People also ask

How do I overwrite interface TypeScript?

Use the Omit utility type to override the type of an interface property, e.g. interface SpecificLocation extends Omit<Location, 'address'> {address: newType} . The Omit utility type constructs a new type by removing the specified keys from the existing type.

How would you implement an interface to another interface in TypeScript?

An interface can be extended by other interfaces. In other words, an interface can inherit from other interface. Typescript allows an interface to inherit from multiple interfaces. Use the extends keyword to implement inheritance among interfaces.

Can we extend interface in TypeScript?

TypeScript lets you augment an interface by simply declaring an interface with an identical name and new members. This lets you extend existing JavaScript code without creating a new named type.

Which is better type or interface in TypeScript?

Hence interface works better when using objects and method objects. Therefore it is our choice to choose between types or interface according to the program needs. In Typescript, union types can contain one or more types using the “ | ” keyword, where we can have a new union type by combining two interfaces.


2 Answers

I recently ran into this issue and managed to resolve it by creating an index.d.ts in my src folder to overwrite res.locals, my implementation looked like this:

// src/index.d.ts
import 'express';

interface Locals {
  message?: string;
}

declare module 'express' {
  export interface Response  {
    locals: Locals;
  }
}

Make sure you also have it included in your tsconfig.json, e.g

// somewhere in your tsconfig.json
  "include": [
    "src/**/*.ts"
  ]

You would use the interface just as you would normally

import { Request, Response, NextFunction } from 'express';

export const handler = (req: Request, res: Response, next: NextFunction) => {
  // should be typed
  res.locals.message = 'hello'
}

Hope this helps!

like image 99
Faris Tangastani Avatar answered Oct 12 '22 01:10

Faris Tangastani


Unfortunately there is no way to override any using interface merging. You can so some surgery on the type and replace the type using mapped and conditional types:

import * as express from 'express'

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

type MyResponse = Omit<express.Response, "locals"> & { 
  locals: {
    myVar: number
  }
}
function middleware(
  req: express.Request, 
  res: MyResponse, 
  next: express.NextFunction
) {
  res.locals.myVar = '10' // error now
  next()
}
like image 39
Titian Cernicova-Dragomir Avatar answered Oct 12 '22 01:10

Titian Cernicova-Dragomir