我正在尝试使用typescript添加一个属性来表示来自中间件的请求对象。但是,我无法弄清楚如何向对象添加额外的属性。如果可能的话,我宁愿不使用括号表示法。
我正在寻找一种解决方案,可以让我写一些类似的内容(如果可能的话):
05-22 20:32:52.019 24076-24076/com.example.odai.playwithme E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.odai.playwithme, PID: 24076
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.odai.playwithme/com.example.odai.playwithme.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2484)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2544)
at android.app.ActivityThread.access$900(ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1394)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:168)
at android.app.ActivityThread.main(ActivityThread.java:5845)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.example.odai.playwithme.MainActivity.onCreate(MainActivity.java:22)
at android.app.Activity.performCreate(Activity.java:6248)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1125)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2437)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2544)
at android.app.ActivityThread.access$900(ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1394)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:168)
at android.app.ActivityThread.main(ActivityThread.java:5845)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
答案 0 :(得分:65)
您想要创建自定义定义,并使用名为Declaration Merging的Typescript中的功能。这是常用的,例如在method-override
。
创建文件custom.d.ts
,并确保将其包含在tsconfig.json
files
- 部分(如果有)中。内容如下:
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)
})
答案 1 :(得分:38)
您只需向全局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!'))
此处介绍了扩展全局命名空间:https://basarat.gitbooks.io/typescript/docs/types/lib.d.ts.html
答案 2 :(得分:14)
(与其他人一样)被接受的答案对我不起作用
declare module 'express' {
interface Request {
myProperty: string;
}
}
没有。希望能对某人有所帮助。
答案 3 :(得分:8)
在TypeScript中,接口是开放式的。这意味着只需重新定义属性,就可以从任何地方为它们添加属性。
考虑到您使用的是此express.d.ts文件,您应该可以重新定义请求界面以添加额外字段。
interface Request {
property: string;
}
然后在您的中间件功能中, req 参数也应具有此属性。您应该能够在不对代码进行任何更改的情况下使用它。
答案 4 :(得分:8)
所提供的解决方案都不适合我。我最终只是扩展了Request接口:
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 = '';
}
答案 5 :(得分:7)
所有这些响应似乎都是错误的,或者在某种程度上已经过时了。
这在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" ]
干杯。
答案 6 :(得分:3)
一种可能的解决方案是使用“双重铸造到任何”
1-定义与您的属性的接口
export interface MyRequest extends http.IncomingMessage {
myProperty: string
}
2-double cast
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
答案 7 :(得分:2)
如果您尝试了所有答案但仍然无法正常工作,这是一个简单的技巧
app.use((req, res, next) => {
(req as any).property = setProperty();
next();
});
这会将 req
对象强制转换为 any
,因此您可以添加您想要的任何属性。
请记住,这样做会使 req
对象失去类型安全性。
答案 8 :(得分:2)
在 express 4.17.1 中,https://stackoverflow.com/a/55718334/9321986 和 https://stackoverflow.com/a/58788706/9321986 的组合起作用了:
在types/express/index.d.ts
中:
declare module 'express-serve-static-core' {
interface Request {
task?: Task
}
}
并在tsconfig.json
中:
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
答案 9 :(得分:1)
For newer versions of express, you need to augment the express-serve-static-core
module.
This is needed because now the Express object comes from there: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19
Basically, use the following:
declare module 'express-serve-static-core' {
interface Request {
myField?: string
}
interface Response {
myField?: string
}
}
答案 10 :(得分:1)
对我有用的简单解决方案是创建一个扩展快速请求的新自定义界面。 此接口应包含所有自定义 req 属性作为可选。 将此接口设置为中间件请求的类型。
// ICustomRequset.ts
import { Request } from "express"
export default interface ICustomRequset extends Request {
email?: string;
roles?: Array<string>;
}
// AuthMiddleware.ts
...
export default async function (
req: ICustomRequset,
res: Response,
next: NextFunction
) {
try {
// jwt code
req.email=jwt.email
req.roles=jwt.roles
next()
}catch(err){}
答案 11 :(得分:1)
在具有节点12.19.0和Express 4的Mac上,使用Passport进行身份验证,我需要扩展我的Session对象
与上述类似,但略有不同:
import { Request } from "express";
declare global {
namespace Express {
export interface Session {
passport: any;
participantIds: any;
uniqueId: string;
}
export interface Request {
session: Session;
}
}
}
export interface Context {
req: Request;
user?: any;
}```
答案 12 :(得分:1)
这是使用Nestjs和Express时对我有用的。截至2020年11月。
创建文件:./@ types / express-serve-static-core / index.d.ts
注意:必须具有完全相同的路径和文件名。为使Typescript声明合并生效,文件名及其路径必须与原始声明文件和路径匹配。
import { UserModel } from "../../src/user/user.model";
declare global{
namespace Express {
interface Request {
currentUser: UserModel
}
}
}
将此添加到您的tsconfig.json
"typeRoots": [
"@types",
"./node_modules/@types",
]
答案 13 :(得分:1)
这个答案可能已经很晚了,但是无论如何,这就是我的解决方法:
tsconfig
文件中(这可能是一个全新的话题)express
express
目录中创建一个文件并将其命名为index.d.ts
(必须完全一样)declare module 'express' {
export interface Request {
property?: string;
}
}
答案 14 :(得分:1)
在2020年5月下旬尝试扩展ExpressJS的请求时,对我有用的是要帮助任何正在寻找其他尝试的人。要使此功能生效,我必须尝试了十几种方法:
VIPER
"typeRoots": [
"./node_modules/@types",
"./your-custom-types-dir"
]
declare global {
namespace Express {
interface Request {
customBasicProperty: string,
customClassProperty: import("../path/to/CustomClass").default;
}
}
}
答案 15 :(得分:1)
这个答案对那些依赖npm软件包ts-node
的人是有益的。
我也一直在担心扩展 request 对象的问题,我在堆栈溢出中遵循了很多答案,并最终遵循了以下提到的策略。
我在以下目录中声明了 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
编译器出了问题。
在script
中更新开始package.json
对我来说是固定的。
"start": "ts-node --files api/index.ts",
--files
自变量在确定自定义类型定义方面起着关键作用。
有关更多信息,请访问:https://github.com/TypeStrong/ts-node#help-my-types-are-missing
答案 16 :(得分:1)
尝试了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 =
});
答案 17 :(得分:1)
也许这个问题已经解决了,但我只想分享一点,
现在有时候,像其他答案一样的界面可能过于严格,
但是我们实际上可以维护所需的属性,然后通过创建类型为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();
});
现在,我们还可以向该对象添加任何其他所需的属性。
答案 18 :(得分:1)
虽然这是一个非常老的问题,但最近我偶然发现了这个问题。可以接受的答案很好,但是我需要在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 {
...
}
答案 19 :(得分:0)
这实际上不是直接回答问题,但我提供了一种替代方法。我在同一个问题上苦苦挣扎,并尝试了此页面上几乎所有扩展接口的解决方案,但没有一个起作用。
这让我停下了思考:“ 为什么我实际上在修改请求对象?”。
Express开发人员似乎已经认为用户可能想要添加自己的属性。这就是为什么有一个locals
对象的原因。问题在于,它不在request
对象中,而在response
对象中。
response.locals
对象可以包含您可能希望拥有的任何自定义属性,它们封装在请求-响应周期中,因此不会暴露给来自不同用户的其他请求。
需要存储userId吗?只需设置response.locals.userId = '123'
。无需为输入而烦恼。
它的缺点是您必须传递响应对象,但是很有可能您已经在做它了。
答案 20 :(得分:0)
如果您正在寻找适用于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的引用
答案 21 :(得分:0)
这对我有用:
declare namespace e {
export interface Request extends express.Request {
user:IUserReference,
[name:string]:any;
}
export interface Response extends express.Response {
[name:string]:any;
}
}
export type AsyncRequestHandler = (req:e.Request, res:e.Response, logger?:Logger) => Promise<any>|Promise<void>|void;
export type AsyncHandlerWrapper = (req:e.Request, res:e.Response) => Promise<void>;
我在代码中使用它,例如以这种方式导出具有此类签名的函数:
app.post('some/api/route', asyncHandlers(async (req, res) => {
return await serviceObject.someMethod(req.user, {
param1: req.body.param1,
paramN: req.body.paramN,
///....
});
}));
答案 22 :(得分:-8)
为什么我们可以这样做而为什么需要做很多麻烦,就像上面接受的答案一样?
我们可以将其附加到请求标头
,而不是将我们的媒体资源附加到请求 req.headers[property] = "hello"