I've created a module factory function with returns a class annotated by @Module({})
. My problem is, the class depends on function arguments providerToken
and strategy
so I cannot move it outside the function. When I run the code, it works perfectly fine but the value for forRoot
and forRootAsync
is not properly checked for types. Infact typescript doesn't throw any error about that. Also what should be my return value of the function. I've put it any
to avoid errors for now.
This is how I'm using the function to create a module
const TwitterAuthModule =
createHybridAuthModule<TwitterAuthModuleOptions>(
TWITTER_HYBRID_AUTH_OPTIONS,
TwitterAuthStrategy
);
Module creator factory
export function createHybridAuthModule<T>(
providerToken: string,
strategy: any
): any {
@Module({})
class NestHybridAuthModule {
static forRoot(options: T): DynamicModule {
return {
module: NestHybridAuthModule,
providers: [
{
provide: providerToken,
useValue: options,
},
strategy,
],
};
}
static forRootAsync(
options: ModuleAsyncOptions<ModuleOptionsFactory<T>, T>
): DynamicModule {
return {
module: NestHybridAuthModule,
providers: [...this.createAsyncProviders(options), strategy],
};
}
private static createAsyncProviders(
options: ModuleAsyncOptions<ModuleOptionsFactory<T>, T>
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProvider(options)];
}
const useClass = options.useClass as Type<ModuleOptionsFactory<T>>;
return [
this.createAsyncOptionsProvider(options),
{
provide: useClass,
useClass,
},
];
}
private static createAsyncOptionsProvider(
options: ModuleAsyncOptions<ModuleOptionsFactory<T>, T>
): Provider {
if (options.useFactory) {
return {
provide: providerToken,
useFactory: options.useFactory,
inject: options.inject || [],
};
}
const inject = [
(options.useClass || options.useExisting) as Type<
ModuleOptionsFactory<T>
>,
];
return {
provide: providerToken,
useFactory: async (optionsFactory: ModuleOptionsFactory<T>) =>
await optionsFactory.createModuleOptions(),
inject,
};
}
}
return NestHybridAuthModule;
}
Usage of the created module. The value of forRoot is never checked for types
@Module({
imports: [
TwitterAuthModule.forRoot({
consumerKey: '********',
consumerSecret: '******',
callbackURL: '*******',
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
CodePudding user response:
Reason why typescript not check your types is that it treat NestHybridAuthModule
as any
because createHybridAuthModule
function return type is any
. Remove it and allow typescript infer return type.
In case there is --noImplicitReturns
used you can use generic interface
simplified example:
interface INestHybridAuthModule<T> {
forRoot(options: T): DynamicModule
}
export function createHybridAuthModule<T>(
providerToken: string,
strategy: any
): new (...args: any[]) => INestHybridAuthModule<T> {
class NestHybridAuthModule {
forRoot(options: T): DynamicModule {
// logic here
}
}
return NestHybridAuthModule
}
CodePudding user response:
I finally found my answer at https://stackoverflow.com/a/43674389/1029506 and implemented it using a customer decorator:
export interface INestHybridAuthModule<T> {
forRoot(options: T): DynamicModule;
}
function staticImplements<T>() {
return <U extends T>(constructor: U) => {
constructor;
};
}
function createHybridAuthModule<T>(
providerToken: string,
strategy: any
): INestHybridAuthModule<T> {
@Module({})
@staticImplements<INestHybridAuthModule<T>>()
class NestHybridAuthModule {
static forRoot(options: T): DynamicModule {
// code here
}
static forRootAsync(
options: ModuleAsyncOptions<ModuleOptionsFactory<T>, T>
): DynamicModule {
// Code here
}
private static createAsyncProviders(
options: ModuleAsyncOptions<ModuleOptionsFactory<T>, T>
): Provider[] {
// Code here
}
private static createAsyncOptionsProvider(
options: ModuleAsyncOptions<ModuleOptionsFactory<T>, T>
): Provider {
// Code here
}
}
return NestHybridAuthModule;
}
And Finally
const TwitterAuthModule: INestHybridAuthModule<TwitterAuthModuleOptions> = createHybridAuthModule<
TwitterAuthModuleOptions
>(TWITTER_HYBRID_AUTH_OPTIONS, TwitterAuthStrategy);