Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,29 @@ app.route('/:foo/:bar').get<{ foo: string; bar: number }>(req => {
req.params.baz; // $ExpectError
});

// Optional params
app.get('/:foo/:bar?', req => {
req.params.foo; // $ExpectType string
req.params.bar; // $ExpectType string | undefined
});

// Different delimiters
app.get('/:foo/:bar-:baz/:qux', req => {
req.params.foo; // $ExpectType string
req.params.bar; // $ExpectType string
req.params.baz; // $ExpectType string
req.params.qux; // $ExpectType string
req.params.quxx; // $ExpectError
});

// regex parameters - not supported
app.get('/:foo/:bar(\\d:+)/:baz', req => {
req.params.foo; // $ExpectType string
req.params.bar; // $ExpectType string
req.params.qux; // $ExpectType string
req.params.quxx; // $ExpectType string
});

// Query can be a custom type
app.get<{}, any, any, { q: string }>('/:foo', req => {
req.query.q; // $ExpectType string
Expand Down Expand Up @@ -91,7 +114,7 @@ app.get('/nextrouter', (req, res, next) => {

// Next can receive a 'route' parameter to fall back to next route
app.get('/nextroute', (req, res, next) => {
next('route'); // $ExpectType void
next('route'); // $ExpectType void
});

// Default types
Expand Down
29 changes: 19 additions & 10 deletions types/express-serve-static-core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { EventEmitter } from 'events';
import { Options as RangeParserOptions, Result as RangeParserResult, Ranges as RangeParserRanges } from 'range-parser';
import { ParsedQs } from 'qs';

export {};

export type Query = ParsedQs;

export interface NextFunction {
Expand Down Expand Up @@ -96,16 +98,23 @@ export type RequestHandlerParams<
| ErrorRequestHandler<P, ResBody, ReqBody, ReqQuery, Locals>
| Array<RequestHandler<P> | ErrorRequestHandler<P>>;

export type RouteParameterNames<Route extends string> =
string extends Route
? string
: Route extends `${string}:${infer Param}/${infer Rest}`
? (Param | RouteParameterNames<Rest>)
: (Route extends `${string}:${infer LastParam}` ? LastParam : never);

export type RouteParameters<T extends string> = {
[key in RouteParameterNames<T>]: string
};
type GetRouteParameter<RouteAfterColon extends string> = RouteAfterColon extends `${infer Char}${infer Rest}`
? Char extends '/' | '-' | '.'
? ''
: `${Char}${GetRouteParameter<Rest>}`
: RouteAfterColon;

// prettier-ignore
export type RouteParameters<Route extends string> = string extends Route
? ParamsDictionary
: Route extends `${string}(${string}`
? ParamsDictionary //TODO: handling for regex parameters
: Route extends `${string}:${infer Rest}`
? (GetRouteParameter<Rest> extends `${infer ParamName}?`
? { [P in ParamName]?: string }
: { [P in GetRouteParameter<Rest>]: string }) &
(Rest extends `${GetRouteParameter<Rest>}${infer Next}` ? RouteParameters<Next> : unknown)
: { };

export interface IRouterMatcher<
T,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,6 @@ app.route('/:foo/:bar').get<{ foo: string; bar: number }>(req => {
req.params.baz; // $ExpectError
});

// Optional params currently not supported
// TODO: support optional params - https://github.com/DefinitelyTyped/DefinitelyTyped/pull/51262#discussion_r638786417
app.get('/:foo/:bar?', req => {
req.params.foo; // $ExpectType string
req.params['bar?']; // $ExpectType string
});

// Query can be a custom type
app.get<{}, any, any, { q: string }>('/:foo', req => {
req.query.q; // $ExpectType string
Expand Down