diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..713bd2f --- /dev/null +++ b/index.d.ts @@ -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; +} + +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> & { + path: string; + all: IRouterHandler; +} + +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; + +type RequestHandlerParams = + | RouteHandler + | ErrorRequestHandler + | Array; + +interface IRouterMatcher { + (path: PathParams, ...handlers: RouteHandler[]): T; + (path: PathParams, ...handlers: RequestHandlerParams[]): T; +} + +interface IRouterHandler { + (...handlers: RouteHandler[]): T; + (...handlers: RequestHandlerParams[]): T; +} + +type IRouter = Record> & { + param(name: string, handler: RequestParamHandler): IRouter; + param( + callback: (name: string, matcher: RegExp) => RequestParamHandler + ): IRouter; + all: IRouterMatcher; + use: IRouterHandler & IRouterMatcher; + handle: RequestHandler; + route(prefix: PathParams): IRoute; +} + +interface RouterConstructor { + new (options?: RouterOptions): IRouter & RequestHandler; + (options?: RouterOptions): IRouter & RequestHandler; +} + +declare var Router: RouterConstructor; diff --git a/test/types.ts b/test/types.ts new file mode 100644 index 0000000..fc85c83 --- /dev/null +++ b/test/types.ts @@ -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> + type TRes = Expect> + type TNext = Expect> + type P1 = Expect> +}); + +// middleware +router.use((req, res, next) => { + type TReq = Expect> + type TRes = Expect> + type TNext = Expect> + 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> + type TRes = Expect> + type TNext = Expect> + next(); +}) +.get((req, res, next) => { + type TReq = Expect> + type TRes = Expect> + type TNext = Expect> +}); + + +// 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 extends (...args: any[]) => any ? T : { [K in keyof T]: Compute } + +type Equal = (() => T extends Compute ? 1 : 2) extends () => T extends Compute ? 1 : 2 ? true : false + +type Expect = T extends true ? true : never