Typescript를 사용하여 Express Request 객체 확장


128

typescript를 사용하여 미들웨어에서 요청 개체를 표현하는 속성을 추가하려고합니다. 그러나 객체에 추가 속성을 추가하는 방법을 알 수 없습니다. 가능하면 대괄호 표기법을 사용하지 않는 것이 좋습니다.

가능한 경우 이와 비슷한 것을 작성할 수있는 솔루션을 찾고 있습니다.

app.use((req, res, next) => {
    req.property = setProperty(); 
    next();
});

원하는 필드와 함께 express.d.ts 파일이 제공하는 요청 인터페이스를 확장 할 수 있어야합니다.
toskv

답변:


147

사용자 정의 정의를 만들고 선언 병합 이라는 Typescript의 기능을 사용하려고합니다 . 이것은 일반적으로 사용됩니다 method-override.

파일 custom.d.ts을 만들고 tsconfig.jsonfiles섹션 에 포함해야합니다 (있는 경우). 내용은 다음과 같이 보일 수 있습니다.

declare namespace Express {
   export interface Request {
      tenant?: string
   }
}

이렇게하면 코드의 어느 시점에서든 다음과 같은 것을 사용할 수 있습니다.

router.use((req, res, next) => {
    req.tenant = 'tenant-X'
    next()
})

router.get('/whichTenant', (req, res) => {
    res.status(200).send('This is your tenant: '+req.tenant)
})

2
방금이 작업을 수행했지만 tsconfig.json의 파일 섹션에 custom.d.ts 파일을 추가하지 않고 작동했지만 여전히 작동합니다. 예상되는 동작입니까?
Chaim Friedman

1
@ChaimFriedman 예. 이 files섹션은 TypeScript에 포함 된 파일 세트를 제한합니다. 지정하지 않은 경우 files또는 include모든, *.d.ts기본적으로 포함되어 있으므로 사용자 정의 typings이를 추가 할 필요가 없습니다.
interphx

9
나를 위해 작동하지 않음 : Property 'tenant'요청'유형에 존재하지 않습니다.`명시 적으로 포함 tsconfig.json하거나 포함하지 않아도 차이가 없습니다. UPDATEdeclare global@basarat의 pointet 그의 answear 작품 아웃,하지만 난해야 할 일을했을 import {Request} from 'express'첫째.
Lion

5
FWIW,이 답변은 이제 구식 입니다. JCM의 대답은 증대하는 올바른 방법입니다 Requestexpressjs에서 객체 (적어도 4.x의)
에릭 Liprandi

3
향후 검색을 위해
기본적으로

79

주석에서index.d.ts 제안한대로 Express새 멤버를 전역 네임 스페이스에 선언하기 만하면 됩니다. 예:

declare global {
  namespace Express {
    interface Request {
      context: Context
    }
  }
}

전체 예 :

import * as express from 'express';

export class Context {
  constructor(public someContextVariable) {
  }

  log(message: string) {
    console.log(this.someContextVariable, { message });
  }
}

declare global {
  namespace Express {
    interface Request {
      context: Context
    }
  }
}

const app = express();

app.use((req, res, next) => {
  req.context = new Context(req.url);
  next();
});

app.use((req, res, next) => {
  req.context.log('about to return')
  res.send('hello world world');
});

app.listen(3000, () => console.log('Example app listening on port 3000!'))

전역 네임 스페이스 확장 은 내 GitBook에서 자세히 다룹 니다 .


선언에서 글로벌이 필요한 이유는 무엇입니까? 거기에 없으면 어떻게 되나요?
Jason Kuhrt

이것은 인터페이스에서 작동하지만, 누구든지 유형을 병합해야하는 경우 유형이 "닫혀"있고 병합 할 수 없습니다. github.com/Microsoft/TypeScript/issues/…
Peter W

@basarat 씨 저는 맥주를 빚지고 있습니다.
marcellsimon

또한 내 tsconfig.json에 추가해야했습니다. { "compilerOptions": { "typeRoots": [ "./src/typings/", "./node_modules/@types"]}, "files": [ "./ src / typings / express / index.d.ts "]}
marcellsimon

위의 솔루션 중 어느 것도 작동하지 않았습니다. 그러나 이것은 첫 실행에서 작업을 수행했습니다 .. 감사합니다 .. !!
Ritesh

55

최신 버전의 Express의 경우 express-serve-static-core모듈 을 보강해야 합니다.

이제 Express 개체가 https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19 에서 제공되므로 필요합니다.

기본적으로 다음을 사용하십시오.

declare module 'express-serve-static-core' {
  interface Request {
    myField?: string
  }
  interface Response {
    myField?: string
  }
}

1
이것은 나를 위해 일한 반면 평범한 'express'모듈을 확장하면 그렇지 않았습니다. 감사합니다!
Ben Kreeger

4
이 문제를 해결하기 위해이 작업을 수행하기 위해 모듈도 가져와야했습니다.import {Express} from "express-serve-static-core";
andre_b aug

1
@andre_b 힌트 주셔서 감사합니다. import 문이 파일을 모듈로 바꾸는 것 같아요. 이것이 필요한 부분입니다. 나는 export {}또한 작동 하는 사용으로 전환했습니다 .
Danyal Aytekin

2
이 코드가 들어가는 파일이 호출 되지 않았는지 확인하십시오. express.d.ts그렇지 않으면 컴파일러가이를 익스프레스 타이핑에 병합하려고 시도하여 오류가 발생합니다.
Tom Spencer

3
유형이 typeRoots에서 첫 번째 여야합니다. types / express / index.d.ts 및 tsconfig => "typeRoots": [ "./src/types", "./node_modules/@types"]
kaya

31

받아 들인 대답 (다른 사람과 마찬가지로)은 나를 위해 작동하지 않지만

declare module 'express' {
    interface Request {
        myProperty: string;
    }
}

했다. 누군가를 도울 수 있기를 바랍니다.


2
유사한 방법은 "Module Augmentation"의 ts 문서 에 설명되어 있습니다. *.d.ts파일 을 사용하지 않고 유형을 일반 *.ts파일에 저장 하는 경우 유용 합니다.
im.pankratov

3
이것은 나에게도 효과가 있었던 유일한 것입니다. 다른 모든 답변은 .d.ts 파일에 있어야하는 것 같습니다
parliament

custom-declarations.d.tsTypeScript의 프로젝트 루트에 파일을 넣으면 저 에게도 효과적입니다.
focorner

나는 그것을 보존하기 위해 원래의 유형을 확장 : import { Request as IRequest } from 'express/index';interface Request extends IRequest. 또한 typeRoot를 추가했다
벤 크리시에게

17

8 개 정도의 답변을 시도했지만 성공하지 못했습니다. 나는 마지막으로이 작업을 얻을 관리 jd291 에의 코멘트를 가리키는 3mards가 REPO .

라는 기본 파일을 만듭니다 types/express/index.d.ts. 그리고 그것에 쓰십시오.

declare namespace Express {
    interface Request {
        yourProperty: <YourType>;
    }
}

다음과 tsconfig.json함께 포함 :

{
    "compilerOptions": {
        "typeRoots": ["./types"]
    }
}

그런 다음 yourProperty모든 요청에서 액세스 할 수 있어야합니다.

import express from 'express';

const app = express();

app.get('*', (req, res) => {
    req.yourProperty = 
});

14

제공된 솔루션 중 어느 것도 나를 위해 일하지 않았습니다. 요청 인터페이스를 간단히 확장했습니다.

import {Request} from 'express';

export interface RequestCustom extends Request
{
    property: string;
}

그런 다음 사용하려면 :

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

someMiddleware(req: RequestCustom, res: Response, next: NextFunction): void
{
    req.property = '';
}

편집 : 최근 버전의 TypeScript가 이에 대해 불평합니다. 대신 다음을 수행해야했습니다.

someMiddleware(expressRequest: Request, res: Response, next: NextFunction): void
{
    const req = expressRequest as RequestCustom;
    req.property = '';
}

1
그 의지 작업,하지만 당신은 미들웨어 기능, amirite의 100 단위가 아주 자세한 경우
알렉산더 밀스

1
@ user2473015 예, 최신 버전의 Typescript가이 문제를 해결했습니다. 내 업데이트 된 답변을 참조하십시오.
Tom Mettam

8

TypeScript에서 인터페이스는 개방형입니다. 즉, 재정의하기 만하면 어디서나 속성을 추가 할 수 있습니다.

express.d.ts 파일을 사용하고 있다는 점을 고려 하면 요청 인터페이스를 재정 의하여 추가 필드를 추가 할 수 있어야 합니다.

interface Request {
  property: string;
}

그런 다음 미들웨어 함수에서 req 매개 변수 에도이 속성이 있어야합니다. 코드를 변경하지 않고 사용할 수 있어야합니다.


1
코드 전체에서 해당 정보를 어떻게 "공유"합니까? 내가 요청에 속성을 정의하면, 말 Request.user = {};app.ts어떻게 userController.ts그것에 대해 알고?
Nepoxx

2
@Nepoxx 인터페이스를 재정의하면 컴파일러는 속성을 병합하여 모든 곳에서 볼 수 있도록합니다. 이상적으로는 .d.ts 파일에서 재정의하는 것이 좋습니다. :)
toskv

작동하는 것 같지만 express.Handler수동으로 지정하는 대신 유형을 사용하면(req: express.Request, res: express.Response, next: express.NextFunction) => any)Request 내 속성이 존재하지 않는다고 불평하는 것과 동일하게 참조되지 않는 것 같습니다 .
Nepoxx

express.Handler가 요청 인터페이스를 확장하지 않는 한 나는 그것을 기대하지 않을 것입니다. 그래?
toskv

2
을 사용하면 작동 declare module "express"하지만 declare namespace Express. 차라리 네임 스페이스 구문을 사용하고 싶지만 저에게는 작동하지 않습니다.
WillyC

5

이것은 아주 오래된 질문이지만, 나는 lately.The 허용 대답은 괜찮 작동이 문제 우연히하지만 난에 사용자 정의 인터페이스를 추가 할 필요 Request- 내 코드에서 사용되었던 인터페이스와 그 허용과 함께 잘 작동하지 않았다 대답. 논리적으로 나는 이것을 시도했다.

import ITenant from "../interfaces/ITenant";

declare namespace Express {
    export interface Request {
        tenant?: ITenant;
    }
}

그러나 Typescript는 .d.ts파일을 전역 가져 오기로 처리하고 가져 오기가 있으면 일반 모듈로 처리 되기 때문에 작동하지 않았습니다 . 이것이 위의 코드가 표준 타이프 스크립트 설정에서 작동하지 않는 이유입니다.

내가 한 일은 다음과 같습니다.

// typings/common.d.ts

declare namespace Express {
    export interface Request {
        tenant?: import("../interfaces/ITenant").default;
    }
}
// interfaces/ITenant.ts

export interface ITenant {
    ...
}

이것은 내 주 파일에서 작동하지만 내 라우팅 파일이나 컨트롤러에서는 작동하지 않고 linting이 나타나지 않지만 컴파일하려고하면 "속성 '사용자'가 '요청'유형에 존재하지 않습니다."라는 메시지가 표시됩니다. (저는 테넌트 대신 사용자를 사용하고 있습니다)하지만 // @ ts-ignore를 추가하면 작동합니다 (물론 문제를 해결하는 어리석은 방법입니다. 왜 그렇지 않을 수 있는지에 대한 생각이 있습니까? 내 다른 파일을 위해
Logan

@Logan은 매우 이상한 일입니다. 당신은 당신의 공유 할 수 .d.ts, tsconfig.json및 사용 인스턴스를? 또한 글로벌 모듈에서 가져 오기가 TS 2.9부터 만 지원되므로 어떤 버전의 typescript를 사용하고 있습니까? 그게 더 도움이 될 수 있습니다.
16kb

여기에 데이터를 업로드했습니다. pastebin.com/0npmR1Zr 강조 표시가 모두 엉망인 이유를 잘 모르겠습니다. 이것은 주 파일 prnt.sc/n6xsyl 에서 가져온 것입니다. 이것은 다른 파일 prnt.sc/n6xtp0 에서 가져온 것 입니다. 무슨 일이 일어나고 있는지 이해하지만 컴파일러는 이해하지 못합니다. 나는 typescript의 3.2.2 버전을 사용하고 있습니다
Logan

1
놀랍게도 ... "include": [ "src/**/*" ] ...나를 위해 작동하지만 "include": ["./src/", "./src/Types/*.d.ts"],그렇지 않습니다. 나는 이것을 이해하려고 아직 부서에 가지 않았다
16kb

동적 가져 오기를 사용하여 인터페이스를 가져 오는 것이 저에게 효과적입니다. 감사합니다
Roman Mahotskyi

3

이 문제에 대한 답변을 받았을 수도 있지만 조금만 공유하고 싶습니다. 이제는 다른 답변과 같은 인터페이스가 너무 제한적일 수 있지만 실제로 필요한 속성을 유지 한 다음 추가 할 속성을 추가 할 수 있습니다. 유형이 string값 유형 인 키any

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

interface IRequest extends Request {
  [key: string]: any
}

app.use( (req: IRequest, res: Response, next: NextFunction) => {
  req.property = setProperty();

  next();
});

이제이 객체에 원하는 추가 속성을 추가 할 수도 있습니다.


2

express4에서 작동하는 솔루션을 찾고 있다면 다음과 같습니다.

@ types / express / index.d.ts : -------- / index.d.ts 여야합니다.

declare namespace Express { // must be namespace, and not declare module "Express" { 
  export interface Request {
    user: any;
  }
}

tsconfig.json :

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2016",
    "typeRoots" : [
      "@types", // custom merged types must be first in a list
      "node_modules/@types",
    ]
  }
}

에서 참조 https://github.com/TypeStrong/ts-node/issues/715#issuecomment-526757308


2

이러한 모든 응답은 어떤 식 으로든 잘못되었거나 구식 인 것 같습니다.

이것은 2020 년 5 월에 저에게 효과적이었습니다.

에서 ${PROJECT_ROOT}/@types/express/index.d.ts:

import * as express from "express"

declare global {
    namespace Express {
        interface Request {
            my_custom_property: TheCustomType
        }
    }
}

에서 다음 tsconfig.json과 같이 속성을 추가 / 병합합니다.

"typeRoots": [ "@types" ]

건배.


Webpack + Docker에서 작동하며 import *는 export {}로 대체 될 수 있습니다.
Dooomel

1

한 가지 가능한 해결책은 "모든 대상에 이중 캐스팅"을 사용하는 것입니다.

1- 속성으로 인터페이스 정의

export interface MyRequest extends http.IncomingMessage {
     myProperty: string
}

2- 이중 캐스트

app.use((req: http.IncomingMessage, res: http.ServerResponse, next: (err?: Error) => void) => {
    const myReq: MyRequest = req as any as MyRequest
    myReq.myProperty = setProperty()
    next()
})

이중 주조의 장점은 다음과 같습니다.

  • 타이핑이 가능합니다
  • 기존 정의를 오염시키지 않고 확장하여 혼동을 방지합니다.
  • 캐스팅이 명시 적이므로 -noImplicitany플래그로 벌금을 컴파일합니다.

또는 빠른 (입력되지 않은) 경로가 있습니다.

 req['myProperty'] = setProperty()

(자신의 속성으로 기존 정의 파일을 편집하지 마십시오. 유지 관리 할 수 ​​없습니다. 정의가 잘못된 경우 풀 요청을 엽니 다.)

편집하다

이 경우 간단한 캐스팅이 작동합니다. req as MyRequest


이 경우 @akshay는, 그래, 때문에 MyRequest을 확장합니다 http.IncomingMessage. 그것은이 경우 아니었다 통해 더블 캐스팅 any유일한 대안이 될 것입니다
브루노 Grieder

any 대신 unknown으로 캐스트하는 것이 좋습니다.
dev

0

이 답변은 npm 패키지에 의존하는 사람들에게 도움이 될 것 ts-node입니다.

나는 또한 요청 객체 를 확장하는 것과 같은 문제로 어려움을 겪고 있었고 스택 오버플로에서 많은 답변을 따랐으며 아래에 언급 된 전략을 따르는 것으로 끝났습니다.

다음 디렉토리에서 express 에 대한 확장 타이핑을 선언했습니다 .${PROJECT_ROOT}/api/@types/express/index.d.ts

declare namespace Express {
  interface Request {
    decoded?: any;
  }
}

그런 다음 tsconfig.json이와 같은 것으로 업데이트 합니다.

{
  "compilerOptions": {
     "typeRoots": ["api/@types", "node_modules/@types"]
      ...
  }
}

위의 단계를 수행 한 후에도 Visual Studio는 불평을 멈추었지만 불행히도 ts-node컴파일러는 여전히 던졌습니다.

 Property 'decoded' does not exist on type 'Request'.

분명히은 요청에ts-node 대한 확장 유형 정의를 찾을 수 없습니다. 개체에 .

결국 몇 시간을 보낸 후 VS 코드가 불평하지 않고 타이핑 정의를 찾을 수 있었기 때문에 ts-node컴파일러에 문제가 있음을 암시했습니다 .

업데이트 시작 scriptpackage.json나를 위해 수정되었습니다.

"start": "ts-node --files api/index.ts",

--files인수 여기서 중요한 역할을 사용자 정의 유형 정의를 결정 찾을 수 있습니다.

자세한 내용은 https://github.com/TypeStrong/ts-node#help-my-types-are-missing을 방문하십시오.


0

ExpressJS의 요청을 연장하려고 할 때 2020 년 5 월 말에 시도해 볼 다른 것을 찾고있는 사람을 돕는 것이 저에게 효과적이었습니다. 이 작업을 수행하기 전에 12 개 이상의 작업을 시도해야했습니다.

  • tsconfig.json의 "typeRoots"에서 모든 사람이 권장하는 순서를 뒤집습니다 (tsconfig에 "./src"와 같은 rootDir 설정이있는 경우 src 경로를 삭제하는 것을 잊지 마십시오). 예:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • 사용자 지정 확장의 예 ( './your-custom-types-dir/express/index.d.ts "). 인라인 가져 오기 및 기본 내보내기를 사용하여 클래스를 내 경험의 유형으로 사용해야하므로 다음과 같이 표시됩니다.
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • nodemon.json 파일을 업데이트하여 "--files"명령을 ts-node에 추가하십시오. 예 :
{
  "restartable": "rs",
  "ignore": [".git", "node_modules/**/node_modules"],
  "verbose": true,
  "exec": "ts-node --files",
  "watch": ["src/"],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js,json,ts"
}

0

이 답변에 이미 꽤 늦었을 수도 있지만 어쨌든 여기에 내가 해결 한 방법이 있습니다.

  1. tsconfig파일 에 유형 소스가 포함되어 있는지 확인하십시오 (완전히 새로운 스레드 일 수 있음).
  2. 유형 디렉토리 내에 새 디렉토리를 추가하고 유형을 확장하거나 생성하려는 패키지로 이름을 지정합니다. 이 특정 경우에는 다음과 같은 이름으로 디렉토리를 생성합니다.express
  3. express디렉토리 내에서 파일을 만들고 이름을 지정합니다 index.d.ts(정확히 일치해야 함).
  4. 마지막으로 유형을 확장하려면 다음과 같은 코드를 입력하면됩니다.
declare module 'express' {
    export interface Request {
        property?: string;
    }
}

-2

우리가 그냥이 일을하면서 벗어날 수 있는데 왜 우리는 위에서 받아 들인 대답들처럼 그렇게 많은 번거 로움을해야합니까?

request에 속성을 연결하는 대신 요청 헤더에 연결할 수 있습니다.

   req.headers[property] = "hello"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.