NestJS同目录模块使用自定义Decorator触发TypeError问题排查
NestJS自定义装饰器在同目录模块中报错TypeError: not a function
我用NestJS构建API,集成Redis后编写了Dynamic Module用于建立Redis连接,同时自定义了InjectRedisGraph装饰器来获取Redis连接实例。当前遇到的问题是:该装饰器在其他服务中可以正常使用,但在同目录的RedisGraphModule中使用时,会抛出如下TypeError:
@InjectRedisGraph(MainGraph) private readonly graph: Graph, ^ TypeError: (0 , redis_graph_connection_decorator_1.InjectRedisGraph) is not a function
已尝试的解决方案
- 使用绝对路径和相对路径导入装饰器,均失败
- 装饰器文件中导出的普通
console.log函数可正常执行 - 为Module添加
@Injectable装饰器,无效果(装饰器本身未抛出错误) - 将装饰器声明移入Module文件可正常运行,但希望保持文件分离
补充说明:编译后的JS代码中装饰器导出正常;VS Code可解析所有类/函数;tsc编译无错误;使用NestJS默认配置。我是NestJS和TypeScript装饰器新手,希望找到问题根源。
相关代码
装饰器代码(redis-graph-connection.decorator.ts)
import { Inject } from '@nestjs/common' import { RedisClientIdentifier } from './redis-graph-connection.provider' /** * 简化RedisGraph实例注入的装饰器 */ export const InjectRedisGraph = (graph: symbol): ParameterDecorator => { return Inject(graph) } export function doesExport() { console.log("It does") }
模块代码(redis-graph-connection.module.ts)
import { DynamicModule, Logger, OnApplicationShutdown } from '@nestjs/common' import { createRedisGraphProviders } from './redis-graph-connection.provider' import { RedisClientOptions } from '@redis/client' import { doesExport, InjectRedisGraph } from './redis-graph-connection.decorator' import { MainGraph } from '../redis-store.constants' import { Graph } from '@redis/graph' export class RedisGraphModule implements OnApplicationShutdown { static logger = new Logger('redis-graph-client') static async forRootAsync( options: RedisGraphModuleOptions, ): Promise<DynamicModule> { const providers = await createRedisGraphProviders(options) return { module: RedisGraphModule, providers, exports: providers, } } // 此处抛出TypeError: not a function constructor(@InjectRedisGraph(MainGraph) private readonly graph: Graph) { doesExport() } onApplicationShutdown() { // RedisGraph实例清理逻辑 } }
正常工作的服务代码(redis-store.service.ts)
import { Injectable, Logger } from '@nestjs/common' import { EvidenceStore } from 'src/common/evidence-store.interface' import { InjectRedisGraph } from 'src/redis-store/redis-graph-connection/redis-graph-connection.decorator' import { MainGraph } from './redis-store.constants' import { Graph } from '@redis/graph' @Injectable() export class RedisStore implements EvidenceStore { private readonly logger = new Logger('redis-graph-collector') constructor( @InjectRedisGraph(MainGraph) private readonly redisGraph: Graph, ) {} // 业务逻辑代码 ... }
Provider代码(redis-graph-connection.provider.ts)
import { createClient, Graph } from 'redis' import { Provider } from '@nestjs/common' import { RedisGraphModule, RedisGraphModuleOptions } from './redis-graph-connection.module' export const RedisClientIdentifier = Symbol('redisClient') export async function createRedisGraphProviders( options: RedisGraphModuleOptions, ): Promise<Array<Provider>> { const client = createClient(options.redisOptions) client.on('error', (err: Error) => { RedisGraphModule.logger.error(err.message, err.stack) }) await client.connect() RedisGraphModule.logger.log('Connected') const providers: Array<Provider> = [ { provide: RedisClientIdentifier, useValue: client, }, ] options.graphs.forEach((graphName) => { providers.push({ provide: graphName, useValue: new Graph(client, graphName.toString()), }) RedisGraphModule.logger.log(`Created graph for ${graphName.toString()}`) }) return providers }
问题原因与解决方案
问题根源
这是循环导入导致的加载顺序问题:
RedisGraphModule导入装饰器文件- 装饰器文件导入
redis-graph-connection.provider - Provider文件又导入
RedisGraphModule
当NestJS初始化模块时,循环引用会导致装饰器在模块初始化阶段未被正确加载,从而出现"not a function"错误。
解决步骤
1. 打破循环导入
- 将
RedisClientIdentifier从Provider文件移至独立的常量文件(如redis-graph.constants.ts),让装饰器和Provider均从该文件导入,避免互相引用。 - 调整Provider文件的日志逻辑,不再依赖
RedisGraphModule的静态logger,改为独立创建Logger实例。
2. 调整模块初始化逻辑
模块类本身无需通过构造函数注入Graph实例,可通过注入Redis客户端实例来处理应用关闭时的清理逻辑。
修改后的示例代码
新增常量文件(redis-graph.constants.ts)
export const RedisClientIdentifier = Symbol('redisClient')
装饰器文件修改
import { Inject } from '@nestjs/common' import { RedisClientIdentifier } from './redis-graph.constants' export const InjectRedisGraph = (graph: symbol): ParameterDecorator => { return Inject(graph) } export function doesExport() { console.log("It does") }
Provider文件修改
import { createClient, Graph } from 'redis' import { Provider, Logger } from '@nestjs/common' import { RedisClientIdentifier } from './redis-graph.constants' const logger = new Logger('redis-graph-client') export async function createRedisGraphProviders( options: RedisGraphModuleOptions, ): Promise<Array<Provider>> { const client = createClient(options.redisOptions) client.on('error', (err: Error) => { logger.error(err.message, err.stack) }) await client.connect() logger.log('Connected') const providers: Array<Provider> = [ { provide: RedisClientIdentifier, useValue: client, }, ] options.graphs.forEach((graphName) => { providers.push({ provide: graphName, useValue: new Graph(client, graphName.toString()), }) logger.log(`Created graph for ${graphName.toString()}`) }) return providers }
模块文件修改
import { DynamicModule, Logger, OnApplicationShutdown, Inject } from '@nestjs/common' import { createRedisGraphProviders } from './redis-graph-connection.provider' import { RedisClientOptions } from '@redis/client' import { MainGraph } from '../redis-store.constants' import { RedisClientIdentifier } from './redis-graph.constants' export class RedisGraphModule implements OnApplicationShutdown { static logger = new Logger('redis-graph-client') constructor(@Inject(RedisClientIdentifier) private readonly client: any) {} static async forRootAsync( options: RedisGraphModuleOptions, ): Promise<DynamicModule> { const providers = await createRedisGraphProviders(options) return { module: RedisGraphModule, providers, exports: providers, } } onApplicationShutdown() { return this.client.quit() } }
打破循环导入后,装饰器即可在模块中正常使用。
内容的提问来源于stack exchange,提问作者Lukas
相关产品推荐
相关产品推荐

