blakeembrey / compose-middleware

Compose an array of middleware into a single function for use in Express, Connect, router, etc
MIT License
91 stars 8 forks source link

Invalid type inference with flatten.NestedArray #17

Closed smolijar closed 6 years ago

smolijar commented 6 years ago

Using v 5.0.0

Problem description

In the following snippet, typescript template arguments are not inferred correctly.

I expose the compose fn as pipeMiddleware, accepting flat array spreaded arguments:

import { Request, Response } from 'express';
import { compose, Middleware } from 'compose-middleware';

const pipeMiddleware = (...middlewares: Array<Middleware<Request, Response>>) => compose(middlewares);

This works in typescript@3.1.6 flawlessly, however, in current typescript@3.2.1 it produces the following error:

error TS2345: Argument of type 'Middleware<Request, Response, void>[]' is not assignable to parameter of type 'Handler<Error, Request, void>'.
  Type 'Middleware<Request, Response, void>[]' is not assignable to type 'Middleware<Error, Request, void>[]'.
    Type 'Middleware<Request, Response, void>' is not assignable to type 'Middleware<Error, Request, void>'.
      Type 'RequestHandler<Request, Response, void>' is not assignable to type 'Middleware<Error, Request, void>'.
        Type 'RequestHandler<Request, Response, void>' is not assignable to type 'RequestHandler<Error, Request, void>'.
          Types of parameters 'req' and 'req' are incompatible.
            Type 'Error' is not assignable to type 'Request'.

Notice the first line, type template parameters (Handler<Error, Request, void>) are incorrectly inferred.

Temp solution

Declaring template arguments explicitly does the trick for my case.

const pipe = (...middlewares: Array<Middleware<Request, Response>>) => compose<Request, Response>(middlewares);

Assumed cause

This might be a bug in flatten types.

If compose-middleware's type Handler is changed from

type Handler<T, U, V = void> = Middleware<T, U, V> | flatten.NestedArray<Middleware<T, U, V>>;

to

type Handler<T, U, V = void> = Middleware<T, U, V> | Array<Middleware<T, U, V>>;

it works flawlessly again (I use only flat simple arrays in my app.)

smolijar commented 6 years ago

2.8.3 :bug: 2.9.2 :bug: 3.1.6 :ok: 3.2.1 :bug:

I ran through some TS versions, the issue does not appear in 3.1.6 but does in versions before and after it. Hope it helps.

blakeembrey commented 6 years ago

@grissius Thanks for the report. I'll take a look at fixing the type assignability here, or just remove array-flatten if I don't figure it out.

blakeembrey commented 6 years ago

@grissius I think I have the solution here - I'll update array-flatten to take an interface of ReadonlyArray so the mutation interfaces won't be considered (e.g. the issue here is Array#pop() and NestedArray#pop() would return incompatible types).

blakeembrey commented 6 years ago

Released with https://github.com/blakeembrey/array-flatten/releases/tag/v2.1.2, it's a sub-dependency though so I'll look at pushing out a new patch for this library too with the dependency upgraded.