DEV Community

Atul Anand Oraon
Atul Anand Oraon

Posted on

Typescript extending types screws you at the most unexpected time

So as we all get the issue with types. I too got it.

What happened to me?

So I was creating a simple express server. Things were fine until I got this big error message.


pnpm dev

> @ dev D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend
> cross-env NODE_ENV=DEV tsx watch src/index.ts

D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\express@5.1.0\node_modules\express\lib\application.js:161
    res.setHeader('X-Powered-By', 'Express');
        ^

TypeError: Cannot read properties of undefined (reading 'setHeader')
    at Function.handle (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\express@5.1.0\node_modules\express\lib\application.js:161:9)
    at app (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\express@5.1.0\node_modules\express\lib\express.js:38:9)
    at express (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\src\app.ts:34:21)       
    at Object.<anonymous> (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\src\app.ts:41:16)
    at Module._compile (node:internal/modules/cjs/loader:1358:14)
    at Object.transformer (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\tsx@4.20.6\node_modules\tsx\dist\register-D46fvsV_.cjs:3:1104)
    at Module.load (node:internal/modules/cjs/loader:1208:32)
    at Module._load (node:internal/modules/cjs/loader:1024:12)
    at Module.require (node:internal/modules/cjs/loader:1233:19)
    at require (node:internal/modules/helpers:179:18)

Node.js v20.15.1
Enter fullscreen mode Exit fullscreen mode

I was confused I tried to rerun

tsconfig.json

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "lib": ["es6", "dom"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "typeRoots": [
      "./node_modules/@types", // Include default @types
      "./src/types" // Path to your custom types directory
    ]
  },
  "include": ["src/**/*.ts", "src/types/**/*.d.ts"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}
Enter fullscreen mode Exit fullscreen mode

backend\src\types\express.d.ts

import { User } from "../generated/prisma";


declare global  {
    namespace Express{
        interface Request {
            user?: User| null;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

pnpm dev

> @ dev D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend
> cross-env NODE_ENV=DEV nodemon src/index.ts

[nodemon] 3.1.9
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: ts,json
[nodemon] starting `ts-node src/index.ts`
D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\ts-node@10.9.2_@types+node@24.7.2_typescript@5.9.3\node_modules\ts-node\src\index.ts:859
    return new TSError(diagnosticText, diagnosticCodes, diagnostics);
           ^
TSError: ⨯ Unable to compile TypeScript:
src/middlewares/auth.middleware.ts:33:9 - error TS2339: Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.

33     req.user = user;
           ~~~~
src/middlewares/auth.middleware.ts:47:14 - error TS2339: Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.

47     if (!req.user) {
                ~~~~
src/middlewares/auth.middleware.ts:51:36 - error TS2339: Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.

51     if ( requiredRole.includes(req.user.role) === false ) {
                                      ~~~~

    at createTSError (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\ts-node@10.9.2_@types+node@24.7.2_typescript@5.9.3\node_modules\ts-node\src\index.ts:859:12)
    at reportTSError (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\ts-node@10.9.2_@types+node@24.7.2_typescript@5.9.3\node_modules\ts-node\src\index.ts:863:19)
    at getOutput (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\ts-node@10.9.2_@types+node@24.7.2_typescript@5.9.3\node_modules\ts-node\src\index.ts:1077:36)
    at Object.compile (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\ts-node@10.9.2_@types+node@24.7.2_typescript@5.9.3\node_modules\ts-node\src\index.ts:1433:41)
    at Module.m._compile (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\ts-node@10.9.2_@types+node@24.7.2_typescript@5.9.3\node_modules\ts-node\src\index.ts:1617:30)
    at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
    at Object.require.extensions.<computed> [as .ts] (D:\My Code\Projects\web-dev\full-stack\LEARNING\mycom\backend\node_modules\.pnpm\ts-node@10.9.2_@types+node@24.7.2_typescript@5.9.3\node_modules\ts-node\src\index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1208:32)
    at Function.Module._load (node:internal/modules/cjs/loader:1024:12)
    at Module.require (node:internal/modules/cjs/loader:1233:19) {
  diagnosticCodes: [ 2339, 2339, 2339 ]
}
[nodemon] app crashed - waiting for file changes before starting...

Enter fullscreen mode Exit fullscreen mode

Finally after the hit and trials I got it working.

package.json

scripts:{
"dev": "cross-env NODE_ENV=DEV nodemon --watch 'src/**/*.ts' --exec 'ts-node --files' src/index.ts",
"build": "tsc",
"start": "cross-env NODE_ENV=PROD node dist/index.js"
}
Enter fullscreen mode Exit fullscreen mode

tsconfig.json

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "lib": ["es6", "dom"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "typeRoots": [
      "./node_modules/@types", // Include default @types
      "./src/types" // Path to your custom types directory
    ]
  },  
  "include": ["src/**/*.ts", "src/types/**/*.d.ts"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}
Enter fullscreen mode Exit fullscreen mode

backend\src\types\express\index.d.ts

import { User } from "../../../generated/prisma";

declare module "express" {
  export interface Request { 
    user?: User | null;
  }
} 
Enter fullscreen mode Exit fullscreen mode

Now let me explain the dev script

  • Development Script:

    Always use a dev script like:

    "dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node --files' src/index.ts"

    The --files flag is crucial to load type augmentation files automatically.

  • When you see TS errors like “Property ‘user’ does not exist on type ‘Request’”:

    • Double-check module augmentation syntax.
    • Make sure your dev script includes --files for ts-node.
    • Restart your TypeScript server/IDE after changing types or config.

Top comments (0)