Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: application service types default to any #1566

Merged
merged 1 commit into from
Mar 20, 2021

Conversation

vonagam
Copy link
Member

@vonagam vonagam commented Sep 19, 2019

In #1364 ServiceTypes default changed from any to {} with an intention to make app.service() act more strict when ServiceTypes is provided. Desired outcome and expected type resolution (specified in second comment from that PR) were right, but the solution had a flaw.

When you make ServiceTypes default to {}, Application without parameters (like in HookContext) becomes Application< {} >. And you can't assign Application< {} > to Application< MyServiceTypes >:

const app: Application< MyServiceTypes > = context.app; // error
const app: Application< MyServiceTypes > = context.app as Application< any >; // works

As shown Application< any > does not have such drawback.

To achieve desired outcome right solution is to simply remove second catch-all service() overload, because when ServiceTypes is any first definition is already catch-all and when ServiceTypes is not any then first definition acts as a strict one.

There is a little breaking change (that why it is not a fix, but a feat) since now {} stops acting as wildcard and not a default value.

@deskoh
Copy link
Contributor

deskoh commented Feb 23, 2020

This will enforce compulsory typings for all service.
Currently. if ServiceTypes is not defined, all service will default to any.

Possible to have best of both worlds?

@vonagam
Copy link
Member Author

vonagam commented Feb 24, 2020

This will not enforce compulsory typings.

Here is the code which you can check in typescript playground:

interface ApplicationOld< ServiceTypes = {} > {
  service< L extends keyof ServiceTypes >( location: L ): ServiceTypes[ L ];
  service( location: string ): keyof ServiceTypes extends never ? any : never;
}

interface ApplicationNew< ServiceTypes = any > {
  service< L extends keyof ServiceTypes >( location: L ): ServiceTypes[ L ];
}

const oldUnspecified = undefined as any as ApplicationOld;
const oldSpecified = undefined as any as ApplicationOld< { foo: 'bar' } >;
const newUnspecified = undefined as any as ApplicationNew;
const newSpecified = undefined as any as ApplicationNew< { foo: 'bar' } >;

const oldUnFoo = oldUnspecified.service('foo');
const oldUnBaz = oldUnspecified.service('baz');
const oldSeFoo = oldSpecified.service('foo');
const oldSeBaz = oldSpecified.service('baz'); // never
const oldConvert: ApplicationOld<{ foo: 'bar' }> = oldUnspecified; // error;

const newUnFoo = newUnspecified.service('foo');
const newUnBaz = newUnspecified.service('baz');
const newSeFoo = newSpecified.service('foo');
const newSeBaz = newSpecified.service('baz'); // type cheching error
const newConvert: ApplicationNew<{ foo: 'bar' }> = oldUnspecified;

@deskoh
Copy link
Contributor

deskoh commented Feb 29, 2020

Ah. I get it now.

@daffl daffl closed this Jul 11, 2020
@vonagam
Copy link
Member Author

vonagam commented Jul 11, 2020

Was this closed accidentally too?

@daffl daffl added this to the v5 (Dove) milestone Jul 11, 2020
@daffl
Copy link
Member

daffl commented Jul 11, 2020

Dang, yes, sorry. This should also stay open to be either merged or revisited for v5.

@daffl daffl reopened this Jul 11, 2020
@vonagam vonagam changed the base branch from master to dove March 20, 2021 05:56
@vonagam
Copy link
Member Author

vonagam commented Mar 20, 2021

Rebased and adapted.

@daffl
Copy link
Member

daffl commented Mar 20, 2021

Great, thank you! Will have a look shortly but if it doesn't break any of the latest tests this should work well.

@daffl daffl merged commit d93ba9a into feathersjs:dove Mar 20, 2021
@daffl
Copy link
Member

daffl commented Mar 20, 2021

Perfect! This totally should fix some of the problems I was having before, too. Will go out with the beta release shortly.

@daffl
Copy link
Member

daffl commented Mar 20, 2021

Hm, strange, looked like the build passed but is failing once it was merged.

@vonagam
Copy link
Member Author

vonagam commented Mar 20, 2021

Any particular problem?

@daffl
Copy link
Member

daffl commented Mar 20, 2021

Type incompatibility: https://github.com/feathersjs/feathers/runs/2156522850?check_suite_focus=true#step:6:235

Getting the errors locally with the latest dove branch, too 🤔

@daffl
Copy link
Member

daffl commented Mar 20, 2021

I have a feeling this might be something else. The compilation error I'm getting locally has something to do with the grant package.

@daffl
Copy link
Member

daffl commented Mar 20, 2021

Other problem fixed for now and I am getting the same error:

Error: test/index.test.ts(27,11): error TS2322: Type 'import("/home/runner/work/feathers/feathers/packages/express/src/declarations").Application<any, any>' is not assignable to type 'import("/home/runner/work/feathers/feathers/packages/feathers/lib/declarations").Application<any, any>'.
  The types returned by 'service(...)' are incompatible between these types.
    Type 'import("/home/runner/work/feathers/feathers/packages/feathers/lib/declarations").FeathersService<import("/home/runner/work/feathers/feathers/packages/express/src/declarations").Application<any, any>, import("/home/runner/work/feathers/feathers/packages/feathers/lib/declarations").Service<any, Partial<any>>>' is not assignable to type 'import("/home/runner/work/feathers/feathers/packages/feathers/lib/declarations").FeathersService<import("/home/runner/work/feathers/feathers/packages/feathers/lib/declarations").Application<any, any>, import("/home/runner/work/feathers/feathers/packages/feathers/lib/declarations").Service<any, Partial<any>>>'.
      Type 'FeathersService<Application<any, any>, Service<any, Partial<any>>>' is not assignable to type 'ServiceAddons<Application<any, any>, Service<any, Partial<any>>>'.
        Types of property 'hooks' are incompatible.
          Type '(options: HookOptions<Application<any, any>, Service<any, Partial<any>>>) => 

Problem seems to be that the Express app type is no longer compatible with the basic Feathers app type (apparently it was before for some reason).

I tried updating express/src/declarations.ts with the following:

interface ExpressUseHandler<T, ServiceTypes> {
  <L extends keyof ServiceTypes & string> (
    path: L,
    ...middlewareOrService: (
      Express|express.RequestHandler|
      (keyof any extends keyof ServiceTypes ? ServiceInterface<any> : ServiceTypes[L])
    )[]
  ): T;
  (...expressHandlers: express.RequestHandler[]): T;
  (handler: Express|express.ErrorRequestHandler): T;
}

export interface Application<ServiceTypes = any, AppSettings = any>
  extends FeathersApplication<ServiceTypes, AppSettings>, Omit<Express, 'listen'|(keyof FeathersApplication)> {
    listen(port: number, hostname: string, backlog: number, callback?: () => void): Promise<http.Server>;
    listen(port: number, hostname: string, callback?: () => void): Promise<http.Server>;
    listen(port: number|string|any, callback?: () => void): Promise<http.Server>;
    listen(callback?: () => void): Promise<http.Server>;
    use: ExpressUseHandler<this, ServiceTypes>;
  }

But no luck so far.

@vonagam vonagam deleted the feat-services-types-any branch October 27, 2021 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants