Skip to content

Commit

Permalink
Add typescript definition pillarjs#110
Browse files Browse the repository at this point in the history
  • Loading branch information
suddenlyGiovanni committed Jul 2, 2024
1 parent b5ad46b commit 63196d3
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 0 deletions.
122 changes: 122 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@

import { OutgoingMessage } from "http";

export default Router;

type HttpMethods =
'get' |
'post' |
'put' |
'head' |
'delete' |
'options' |
'trace' |
'copy' |
'lock' |
'mkcol' |
'move' |
'purge' |
'propfind' |
'proppatch' |
'unlock' |
'report' |
'mkactivity' |
'checkout' |
'merge' |
'm-search' |
'notify' |
'subscribe' |
'unsubscribe' |
'patch' |
'search' |
'connect'

export interface RouterOptions {
strict?: boolean;
caseSensitive?: boolean;
mergeParams?: boolean;
}

export interface IncomingRequest {
url?: string;
method?: string;
originalUrl?: string;
params?: Record<string, string>;
}

interface BaseRoutedRequest extends IncomingRequest {
baseUrl: string;
next?: NextFunction;
route?: IRoute;
}

export type RoutedRequest = BaseRoutedRequest & {
[key: string]: any;
}

export interface NextFunction {
(err?: any): void;
}

type IRoute = Record<HttpMethods, IRouterHandler<IRoute>> & {
path: string;
all: IRouterHandler<IRoute>;
}

type RequestParamHandler = (
req: IncomingRequest,
res: OutgoingMessage,
next: NextFunction,
value: string,
name: string
) => void;

export interface RouteHandler {
(req: RoutedRequest, res: OutgoingMessage, next: NextFunction): void;
}

export interface RequestHandler {
(req: IncomingRequest, res: OutgoingMessage, next: NextFunction): void;
}

type ErrorRequestHandler = (
err: any,
req: IncomingRequest,
res: OutgoingMessage,
next: NextFunction
) => void;

type PathParams = string | RegExp | Array<string | RegExp>;

type RequestHandlerParams =
| RouteHandler
| ErrorRequestHandler
| Array<RouteHandler | ErrorRequestHandler>;

interface IRouterMatcher<T> {
(path: PathParams, ...handlers: RouteHandler[]): T;
(path: PathParams, ...handlers: RequestHandlerParams[]): T;
}

interface IRouterHandler<T> {
(...handlers: RouteHandler[]): T;
(...handlers: RequestHandlerParams[]): T;
}

type IRouter = Record<HttpMethods, IRouterMatcher<IRouter>> & {
param(name: string, handler: RequestParamHandler): IRouter;
param(
callback: (name: string, matcher: RegExp) => RequestParamHandler
): IRouter;
all: IRouterMatcher<IRouter>;
use: IRouterHandler<IRouter> & IRouterMatcher<IRouter>;
handle: RequestHandler;
route(prefix: PathParams): IRoute;
}

interface RouterConstructor {
new (options?: RouterOptions): IRouter & RequestHandler;
(options?: RouterOptions): IRouter & RequestHandler;
}

declare var Router: RouterConstructor;
89 changes: 89 additions & 0 deletions test/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { createServer, OutgoingMessage } from 'http';
import Router, {
RouterOptions,
RouteHandler,
NextFunction,
RoutedRequest,
IncomingRequest
} from '..';

const options: RouterOptions = {
strict: false,
caseSensitive: false,
mergeParams: false
};

// new constructor
new Router().all('/', (req, res, next) => {})
// direct call
Router().all('/', (req, res, next) => {})

const router = new Router(options);
const routerHandler: RouteHandler = (req, res, next) => {
res.setHeader('Content-Type', 'plain/text');
res.write('Hello')
res.end('world')
};

// test verb methods
router.get('/', routerHandler);
router.post('/', routerHandler);
router.delete('/', routerHandler);
router.patch('/', routerHandler);
router.options('/', routerHandler);
router.head('/', routerHandler);
router.bind('/', routerHandler);
router.connect('/', routerHandler);
router.trace('/', routerHandler);
router['m-search']('/', routerHandler);


// param
router.param('user_id', (req, res, next, id) => {
type TReq = Expect<Equal<typeof req, IncomingRequest>>
type TRes = Expect<Equal<typeof res, OutgoingMessage>>
type TNext = Expect<Equal<typeof next, NextFunction>>
type P1 = Expect<Equal<typeof id, string>>
});

// middleware
router.use((req, res, next) => {
type TReq = Expect<Equal<typeof req, RoutedRequest>>
type TRes = Expect<Equal<typeof res, OutgoingMessage>>
type TNext = Expect<Equal<typeof next, NextFunction>>
next();
});

// RoutedRequest is extended with properties without type errors
router.use((req, res, next) => {
req.extendable = 'extendable'
next();
});

router.route('/')
.all((req, res, next) => {
type TReq = Expect<Equal<typeof req, RoutedRequest>>
type TRes = Expect<Equal<typeof res, OutgoingMessage>>
type TNext = Expect<Equal<typeof next, NextFunction>>
next();
})
.get((req, res, next) => {
type TReq = Expect<Equal<typeof req, RoutedRequest>>
type TRes = Expect<Equal<typeof res, OutgoingMessage>>
type TNext = Expect<Equal<typeof next, NextFunction>>
});


// valid for router from createServer
createServer(function(req, res) {
router(req, res, (err) => {})
router.handle(req, res, (err) => {})
})


// Type test helper methods
type Compute<T> = T extends (...args: any[]) => any ? T : { [K in keyof T]: Compute<T[K]> }

type Equal<X, Y> = (<T>() => T extends Compute<X> ? 1 : 2) extends <T>() => T extends Compute<Y> ? 1 : 2 ? true : false

type Expect<T extends true> = T extends true ? true : never

0 comments on commit 63196d3

Please sign in to comment.