-
Notifications
You must be signed in to change notification settings - Fork 399
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
Allow passing additional types for Global and Middleware Context #1505
Conversation
Codecov Report
@@ Coverage Diff @@
## main #1505 +/- ##
==========================================
+ Coverage 82.00% 82.07% +0.06%
==========================================
Files 18 18
Lines 1495 1495
Branches 435 435
==========================================
+ Hits 1226 1227 +1
Misses 172 172
+ Partials 97 96 -1
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Adding this should not bring any breaking changes. The only downside of the changes would be the types inside this framework can be even more complex but it is not due to this PR.
src/App-context-types.spec.ts
Outdated
|
||
app.message('hello', async ({ context }) => { | ||
// If the globalContextKey in the context is not explicitly typed, then it will be 'any' | ||
// The IfAny check will then set 'check' to 'never', causing the check.valid call later to fail |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit tricky but as long as the check.valid = true;
statement fails in the compilation phase, it'd be fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a question regarding the type of the private members (mainly for my own education!)
What about documentation? Wondering how we can communicate this to developers via our existing docs site? We have a small section, available by a 'Using Typescript' link in the bottom left of the site. I think we should add more information on this feature in there. I know that 'guide' is very light, but every small improvement helps.
Maybe it is also worth showing off how to use this feature in the TypeScript example in the repo?
public use<MiddlewareCustomContext extends StringIndexed = StringIndexed>( | ||
m: Middleware<AnyMiddlewareArgs, AppCustomContext & MiddlewareCustomContext>, | ||
): this { | ||
this.middleware.push(m as Middleware<AnyMiddlewareArgs>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the private middleware
property also be typed as Middleware<AnyMiddlewareArgs, AppCustomContext & MiddlewareCustomContext>[]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently if it's typed with the Contexts the below compiler error is generated:
TS2345: Argument of type 'Middleware<AnyMiddlewareArgs, AppCustomContext & MiddlewareCustomContext>' is not assignable to parameter of type 'Middleware<AnyMiddlewareArgs, StringIndexed>'.
Type 'StringIndexed' is not assignable to type 'AppCustomContext & MiddlewareCustomContext'.
Type 'StringIndexed' is not assignable to type 'AppCustomContext'.
'StringIndexed' is assignable to the constraint of type 'AppCustomContext', but 'AppCustomContext' could be instantiated with a different subtype of constraint 'StringIndexed'.
In addition, I'm not sure the typing is as important on this line, as once the middlewares are in the private global middlewares, I don't think Bolt cares about the custom context type anymore? From my limited testing, if
(Line: 228)
/** Global middleware chain */
private middleware: Middleware<AnyMiddlewareArgs>[];
is changed to
/** Global middleware chain */
private middleware: Middleware<AnyMiddlewareArgs, AppCustomContext>[];
Then m as Middleware<AnyMiddlewareArgs, AppCustomContext>
will work. But doing this, causes the processMiddleware
function(https://github.com/slackapi/bolt-js/blob/main/src/middleware/process.ts) to want to be aware of the app context as well. Circling back to the question of whether Bolt cares about the context internally, I don't really see a scenario where the context will matter at that point as the consumer of the library only interacts with the middlewares via the use
, shortcut
, message
, etc middleware registration points.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the explanation! I appreciate it; my TS isn't great so this is helpful 🙇
public message< | ||
MiddlewareCustomContext extends StringIndexed = StringIndexed, | ||
>( | ||
filter: MessageEventMiddleware, | ||
...listeners: MessageEventMiddleware<AppCustomContext & MiddlewareCustomContext>[] | ||
): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While building the tests I wasn't able to get TypeScript to use this overload. As filter
and ...listeners
share the same type I'm not sure this overload is necessary. If there's agreement, I feel like the first overloads docs could be updated to go into more detail about how a middleware can be used as a filter.
Lines 567 to 571 in 7eec438
/** * * @param listeners Middlewares that process and react to a message event */ public message(...listeners: MessageEventMiddleware[]): void;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. As long as it still works without the overloaded method, I think that we can safely remove the unnecessary method with filter: MessageEventMiddleware
argument.
Everything in this PR already looks great to @filmaj and me. We'll merge this PR within a few business days. If anyone has comments, please don't hesitate to write in before merging! |
No responses from others. Let me merge this one now. @M1kep Thanks for the great improvement! |
Summary
This pr picks up from #1157 and allows users to pass custom types to the App initialization(Global Context) as well as individual middleware(Localized Context). This allows IDEs to provide more specific suggestions as well as better type checking.
I've included an example of the direction I'm thinking would work for testing this. Testing is a little weird because this is purely a type system addition. As it's just the type system, the only forms of tests I can think of will cause compilation errors rather than test failures. Additionally, as the Context type is still StringIndexed an
IsAny
check was used which was found via StackOverflow. If there are any suggestions for better way to test this, I'd be happy to give it a try, otherwise I can move forward with the test cases using the my current method.Requirements (place an
x
in each[ ]
)