Skip to content

Commit

Permalink
Add typescript definition
Browse files Browse the repository at this point in the history
  • Loading branch information
ilikejames committed Mar 29, 2023
1 parent 86a5f76 commit c89a2d4
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 11 deletions.
43 changes: 34 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,85 +37,101 @@ jobs:
- name: Node.js 0.8
node-version: "0.8"
npm-i: mocha@2.5.3 supertest@1.1.0
npm-rm: nyc
npm-rm: nyc typescript @types/node

- name: Node.js 0.10
node-version: "0.10"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
npm-rm: typescript @types/node

- name: Node.js 0.12
node-version: "0.12"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
npm-rm: typescript @types/node

- name: io.js 1.x
node-version: "1.8"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
npm-rm: typescript @types/node

- name: io.js 2.x
node-version: "2.5"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
npm-rm: typescript @types/node

- name: io.js 3.x
node-version: "3.3"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
npm-rm: typescript @types/node

- name: Node.js 4.x
node-version: "4.9"
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
npm-rm: typescript @types/node

- name: Node.js 5.x
node-version: "5.12"
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
npm-rm: typescript @types/node

- name: Node.js 6.x
node-version: "6.17"
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2
npm-rm: typescript @types/node

- name: Node.js 7.x
node-version: "7.10"
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6
npm-rm: typescript @types/node

- name: Node.js 8.x
node-version: "8.17"
npm-i: mocha@7.2.0
npm-i: mocha@7.2.0 @types/node@^8.0.0

- name: Node.js 9.x
node-version: "9.11"
npm-i: mocha@7.2.0
npm-i: mocha@7.2.0 @types/node@^9.0.0

- name: Node.js 10.x
node-version: "10.24"
npm-i: mocha@8.4.0
npm-i: mocha@8.4.0 @types/node@^10.0.0

- name: Node.js 11.x
node-version: "11.15"
npm-i: mocha@8.4.0
npm-i: mocha@8.4.0 @types/node@^11.0.0

- name: Node.js 12.x
node-version: "12.22"
npm-i: mocha@9.2.2
npm-i: mocha@9.2.2 @types/node@^12.0.0

- name: Node.js 13.x
node-version: "13.14"
npm-i: mocha@9.2.2
npm-i: mocha@9.2.2 @types/node@^13.0.0

- name: Node.js 14.x
node-version: "14.21"
node-version: "14.21"
npm-i: \@types/node@^14.0.0

- name: Node.js 15.x
node-version: "15.14"
npm-i: \@types/node@^15.0.0

- name: Node.js 16.x
node-version: "16.19"
npm-i: \@types/node@^16.0.0

- name: Node.js 17.x
node-version: "17.9"
npm-i: \@types/node@^17.0.0

- name: Node.js 18.x
node-version: "18.14"
npm-i: \@types/node@^18.0.0

- name: Node.js 19.x
node-version: "19.7"
npm-i: \@types/node@^18.0.0 # no 19 as yet


steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -161,7 +177,7 @@ jobs:
fi
- name: Install Node.js dependencies
run: npm install
run: npm install --no-optional

- name: List environment
id: list_env
Expand All @@ -182,6 +198,14 @@ jobs:
npm test
fi
- name: Test type definition
shell: bash
run: |
# testing types requires >= node@8
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -ge 8 ]]; then
npm run test-types
fi
- name: Lint code
if: steps.list_env.outputs.eslint != ''
run: npm run lint
Expand All @@ -194,6 +218,7 @@ jobs:
mkdir ./coverage
mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}"
fi
- name: Upload code coverage
uses: actions/upload-artifact@v3

Expand Down
118 changes: 118 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

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>;
}

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

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;
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,29 @@
"utils-merge": "1.0.1"
},
"devDependencies": {
"@types/node": "^18.0.0",
"after": "0.8.2",
"eslint": "8.34.0",
"eslint-plugin-markdown": "3.0.0",
"finalhandler": "1.2.0",
"mocha": "10.2.0",
"nyc": "15.1.0",
"safe-buffer": "5.2.1",
"supertest": "6.3.3"
"supertest": "6.3.3",
"typescript": "^5.0.0"
},
"files": [
"lib/",
"LICENSE",
"HISTORY.md",
"README.md",
"SECURITY.md",
"index.js"
"index.js",
"index.d.ts"
],
"typesVersions": {
">=4.0": {"*": ["index.d.ts"]}
},
"engines": {
"node": ">= 0.8"
},
Expand All @@ -43,6 +49,7 @@
"test": "mocha --reporter spec --bail --check-leaks test/",
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
"test-cov": "nyc --reporter=text npm test",
"test-types": "tsc --project tsconfig.json --noEmit",
"version": "node scripts/version-history.js && git add HISTORY.md"
}
}
84 changes: 84 additions & 0 deletions test/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
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();
});


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
var server = 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 c89a2d4

Please sign in to comment.