My goals:
- Be able to do app.get([typed options appear here])
- Be able to do
app.service([typed options appear here])
-> this does not happen when using HookContex
In my attempt to solve this I tried the following:
in the declarations.d.ts file:
imports....
import config from '@config/default.json';
declarations...
// A mapping of service names to types. Will be extended in service files.
export interface EntityServices {
users: Service<UserInterface>;
subscribers: Service<SubscriberInterface>;
permissions: Service<PermissionInterface>;
}
type Config = typeof config;
interface ModifiedConfig extends Config {
sequelizeClient: Sequelize.Sequelize;
'view engine': 'ejs';
}
export interface ServiceTypes extends EntityServices {}
type initialApplication = ExpressFeathers<ServiceTypes>;
type AppGet = <T extends keyof ModifiedConfig>(path: T) => ModifiedConfig[T];
type AppSet = <T extends keyof ModifiedConfig>(
path: T,
config: ModifiedConfig[T],
) => initialApplication;
// The application instance type that will be used everywhere else
export interface Application extends Omit<initialApplication, 'get' | 'set'> {
get: AppGet;
set: AppSet;
}
export type Paths = keyof ServiceTypes;
export type EntityPaths = keyof EntityServices;
export interface APIHookContext extends FeathersHookContext {
app: Application;
path: Paths;
}
export interface HookContext extends FeathersHookContext {}
This initially works perfectly but the issue comes in the use:
In a hook function if I change:
This:
export const sendNewUserEmail = async (context: HookContext) => { [some code...] return context; };
To this:
export const sendNewUserEmail = async (context: APIHookContext) => { [some code...] return context; };
I get all of typing BUT, all hell breaks loose with some of the following:
The service.hooks(hooks);
call, now complains of a mismatch between the application and paths object of APIHookContext and HookContext.
On deeper inspection I see within the feathers typing, HookContext refers to "this", which I believe is the uninitialized application. Hence my object is populated while "this" is empty.
I tried further by extracting the service.hooks()
function and updating its type but then the issue arises with feathers imports such as "feathers-hooks-common" functions. They pass HookContext which then does not match with APIHookContext.
A sample of the type error on the service.hooks function:
...type '(((context: HookContext<any, Service<any>>) => Promise<HookContext<any, Service<any>>>) | ((context: APIHookContext) => Promise<...>))[]' is not assignable to type 'Hook<any, Service<any>>[]'. Type '((context: HookContext<any, Service<any>>) => Promise<HookContext<any, Service<any>>>) | ((context: APIHookContext) => Promise<...>)' is not assignable to type 'Hook<any, Service<any>>'. Type '(context: APIHookContext) => Promise<APIHookContext>' is not assignable to type 'Hook<any, Service<any>>'. Types of parameters 'context' and 'hook' are incompatible. Type 'HookContext<any, Service<any>>' is not assignable to type 'APIHookContext'. Types of property 'app' are incompatible. Type 'Application<{}>' is missing the following properties from type 'Application': request, response, init, defaultConfiguration, and 40 more.ts(2345)
I would like to have a consistent application object that is used inside hook methods, class methods etc... that has all of the services and their types and things such as a typed get object etc...
I have been able to achieve this in the declaration.d.ts file but the above conflicts and the reference to "this" is blocking me.
Let me know what is possible and if there is a way to achieve this level of typing.
Regards