Skip to content

Commit 9dd6644

Browse files
authored
Merge pull request #11 from Eliav2:dev
Dev
2 parents e7a827a + 367fa35 commit 9dd6644

File tree

6 files changed

+260
-69
lines changed

6 files changed

+260
-69
lines changed

packages/express-typed/package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
{
22
"name": "express-typed",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"description": "end-to-end typesafe TypeScript wrapper for Express.js",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/Eliav2/express-typed"
8+
},
9+
"homepage": "https://github.com/Eliav2/express-typed",
10+
"bugs": "https://github.com/Eliav2/express-typed/issues",
511
"type": "module",
612
"main": "dist/express-typed.js",
713
"module": "dist/express-typed.js",
@@ -15,13 +21,14 @@
1521
},
1622
"keywords": [],
1723
"author": "",
18-
"license": "ISC",
24+
"license": "MIT",
1925
"devDependencies": {
2026
"@types/express": "^4.17.21",
2127
"@types/express-serve-static-core": "^4.19.0",
2228
"@types/qs": "^6.9.15",
2329
"@vitest/coverage-v8": "^1.5.2",
2430
"express": "^4.19.2",
31+
"supertest": "^7.0.0",
2532
"typescript": "^5.4.2"
2633
},
2734
"peerDependencies": {

packages/express-typed/tests/demo.test-d.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,60 @@
11
import { expectTypeOf, test } from "vitest";
2-
import { GetRouteResponseInfo, ParseRoutes } from "../src/express-typed";
3-
import typedRouter from "./test-router";
2+
import { GetRouteResponseInfo, ParseRoutes, TypedRequest, TypedResponse, TypedRouter } from "../src/express-typed";
43

54
// Test TypedRouter
65
test("TypedRouter", () => {
6+
const typedRouter = new TypedRouter({
7+
// returned type is inferred
8+
"/": {
9+
get: (req, res) => {
10+
return res.send("get: /").status(200);
11+
},
12+
post: (req, res) => {
13+
return res.send("post: /").status(200);
14+
},
15+
},
16+
// request body is explicitly typed, response is inferred based on the return value
17+
"/explicit-req": {
18+
get: (req: TypedRequest<{ body: { name: string } }>, res) => {
19+
const body = req.body;
20+
// ^?
21+
return res.json(req.body).status(200);
22+
},
23+
},
24+
// response body is explicitly typed, retrun type must at least extend { name: string }
25+
"/explicit-res": {
26+
get: (req, res: TypedResponse<{ body: { name: string } }>) => {
27+
return res.json({ name: "eliav" }).status(200);
28+
},
29+
},
30+
// nested router are allowed, and fully typed
31+
"/nested": new TypedRouter({
32+
"/": {
33+
get: (req, res) => {
34+
const test = res.send("get /nested/").status(200);
35+
return test;
36+
},
37+
// async methods are supported
38+
post: async (req, res) => {
39+
const test = (await (await fetch("https://jsonplaceholder.typicode.com/todos/1")).json()) as {
40+
userId: number;
41+
id: number;
42+
title: string;
43+
completed: boolean;
44+
};
45+
return res.json(test).status(200);
46+
},
47+
},
48+
// any of "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head" is allowed as a method
49+
"/all": {
50+
all: (req, res) => {
51+
return res.send("responding to all methods");
52+
},
53+
},
54+
}),
55+
});
56+
57+
758
type AppRoutes = ParseRoutes<typeof typedRouter>;
859

960
type HomeGetResponse = GetRouteResponseInfo<AppRoutes, "/", "get">;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { expect, test, describe } from "vitest";
2+
import { TypedRequest, TypedResponse, TypedRouter } from "../src/express-typed";
3+
import request from "supertest";
4+
import express from "express";
5+
6+
// Test TypedRouter
7+
describe("TypedRouter", () => {
8+
const typedRouter = new TypedRouter({
9+
// returned type is inferred
10+
"/": {
11+
get: (req, res) => {
12+
return res.send("get: /").status(200);
13+
},
14+
post: (req, res) => {
15+
return res.send("post: /").status(200);
16+
},
17+
},
18+
// request body is explicitly typed, response is inferred based on the return value
19+
"/explicit-req": {
20+
get: (req: TypedRequest<{ body: { name: string } }>, res) => {
21+
const body = req.body;
22+
// ^?
23+
return res.json(req.body).status(200);
24+
},
25+
},
26+
// response body is explicitly typed, retrun type must at least extend { name: string }
27+
"/explicit-res": {
28+
get: (req, res: TypedResponse<{ body: { name: string } }>) => {
29+
return res.json({ name: "eliav" }).status(200);
30+
},
31+
},
32+
// nested router are allowed, and fully typed
33+
"/nested": new TypedRouter({
34+
"/": {
35+
get: (req, res) => {
36+
const test = res.send("get /nested/").status(200);
37+
return test;
38+
},
39+
// async methods are supported
40+
post: async (req, res) => {
41+
const test = (await (await fetch("https://jsonplaceholder.typicode.com/todos/1")).json()) as {
42+
userId: number;
43+
id: number;
44+
title: string;
45+
completed: boolean;
46+
};
47+
return res.json(test).status(200);
48+
},
49+
},
50+
// any of "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head" is allowed as a method
51+
"/all": {
52+
all: (req, res) => {
53+
return res.send("responding to all methods");
54+
},
55+
},
56+
}),
57+
});
58+
test("TypedRouter correctly defined", () => {
59+
expect(typedRouter).toBeDefined();
60+
});
61+
test("TypedRouter serves correctly express routes", async () => {
62+
const app = express();
63+
app.use(typedRouter.router);
64+
await request(app)
65+
.get("/")
66+
.expect(200)
67+
.then((res) => {
68+
expect(res.text).toBe("get: /");
69+
});
70+
71+
await request(app)
72+
.post("/")
73+
.expect(200)
74+
.then((res) => {
75+
expect(res.text).toBe("post: /");
76+
});
77+
});
78+
79+
test("TypedRouter serves correctly express nested routes", async () => {
80+
const app = express();
81+
app.use(typedRouter.router);
82+
await request(app)
83+
.get("/nested")
84+
.expect(200)
85+
.then((res) => {
86+
expect(res.text).toBe("get /nested/");
87+
});
88+
89+
await request(app)
90+
.post("/nested")
91+
.expect(200)
92+
.then((res) => {
93+
expect(res.body).toEqual({
94+
userId: 1,
95+
id: 1,
96+
title: "delectus aut autem",
97+
completed: false,
98+
});
99+
});
100+
101+
await request(app)
102+
.options("/nested/all")
103+
.expect(200)
104+
.then((res) => {
105+
expect(res.text).toBe("responding to all methods");
106+
});
107+
});
108+
});

packages/express-typed/tests/express-typed.test.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/express-typed/tests/test-router.ts

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)