Skip to content

Commit f8687cb

Browse files
committed
feat: enable type-aware linting and type checking
Turn on lint.options.typeAware and typeCheck in vite.config.ts and fix everything they surfaced: - void 58 floating promises across web routes and stores; restructure destructures flagged by unbound-method - retype useState<any> and redundant any unions with real DTO types - narrow unknown values before stringification in the mongo migration script and the moderation scan decoder - give package tsconfigs explicit rootDir (or noEmit for @cnode/db) so tsgolint can type-check each project; apps/api keeps its existing nested dist layout - declare *.css modules for side-effect imports in apps/web - upgrade TypeScript to 7.0.2 and @types/node to 24 to match Node 24 Typing @cnode/db's DB export (currently any) surfaces ~40 more type errors in @cnode/api, including likely real bugs in message rendering (topic.author does not exist on the row type); left as a follow-up, see the TODO in packages/db/src/client.ts.
1 parent ca3dc47 commit f8687cb

40 files changed

Lines changed: 783 additions & 197 deletions

‎apps/api/package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@
3838
"devDependencies": {
3939
"@types/bcryptjs": "^2.4.6",
4040
"@types/lodash": "^4.17.24",
41-
"@types/node": "^22.0.0",
41+
"@types/node": "^24.0.0",
4242
"@types/nodemailer": "^8.0.0",
4343
"@types/react": "19.2.2",
4444
"@types/react-dom": "19.2.3",
4545
"@types/uuid": "^10.0.0",
4646
"tsx": "^4.19.0",
47-
"typescript": "^5.7.0",
47+
"typescript": "^7.0.0",
4848
"vite": "catalog:",
4949
"vite-plus": "catalog:"
5050
}

‎apps/api/src/lib/db.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { boolEq, boolValue } from "./db-compat";
2121
import { deleteReplyWithStore, type ReplyDeletionStore } from "./reply-deletion";
2222
import { createReplyWithStore, type ReplyCreationStore } from "./reply-creation";
2323

24-
let dbInstance: DB | null = null;
24+
let dbInstance: DB = null;
2525
const INTERNAL_TABS = ["dev", "test"];
2626

2727
function getDb(): DB {

‎apps/api/src/lib/moderation-scan.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ export function scanDefaults() {
3535
function decodeJsonArray(value: unknown): number[] | null {
3636
if (!value) return null;
3737
if (Array.isArray(value)) return value.map(Number).filter((v) => v > 0);
38+
if (typeof value !== "string") return null;
3839
try {
39-
const parsed = JSON.parse(String(value));
40+
const parsed = JSON.parse(value);
4041
return Array.isArray(parsed) ? parsed.map(Number).filter((v) => v > 0) : null;
4142
} catch {
4243
return null;

‎apps/api/src/middleware/auth.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { roleQueries, userQueries } from "../lib/db";
44
import { resolveUserAccess } from "../lib/user-access";
55

66
export interface AuthVars {
7-
user: Awaited<ReturnType<typeof userQueries.getById>> | null;
7+
user: Awaited<ReturnType<typeof userQueries.getById>>;
88
isLogin: boolean;
99
isAdmin: boolean;
1010
isMod: boolean;

‎apps/api/src/routes/auth.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ auth.openapi(githubCreateRoute, async (c) => {
572572
if (!profile)
573573
return c.json({ success: false as const, error_msg: "GitHub 登录状态已过期,请重新授权" }, 401);
574574
const body = c.req.valid("json");
575-
let user: Awaited<ReturnType<typeof userQueries.getById>> | null = null;
575+
let user: Awaited<ReturnType<typeof userQueries.getById>> = null;
576576
if (body.isnew) {
577577
const loginname = profile.login.toLowerCase();
578578
if (await userQueries.getByLoginName(loginname))
@@ -706,7 +706,7 @@ auth.openapi(githubUnbindRoute, async (c) => {
706706
let result: Awaited<ReturnType<typeof executeGithubUnbind>>;
707707
try {
708708
result = await executeGithubUnbind(user, password, {
709-
clearGithubInfo: userQueries.clearGithubInfo,
709+
clearGithubInfo: (userId, githubId) => userQueries.clearGithubInfo(userId, githubId),
710710
revokeToken: revokeGithubToken,
711711
verifyPassword: bcryptjs.compare,
712712
});
@@ -999,8 +999,8 @@ auth.post("/upload/image", async (c) => {
999999
return c.json({ success: false, error_msg: "只支持 png/jpeg/gif/webp/svg 图片上传" }, 422);
10001000
const maxSize = Number(process.env.OSS_UPLOAD_MAX_BYTES || 5 * 1024 * 1024);
10011001
if (file.size > maxSize) return c.json({ success: false, error_msg: "图片不能超过 5MB" }, 413);
1002-
const purpose =
1003-
typeof formData?.get("purpose") === "string" ? String(formData.get("purpose")) : null;
1002+
const purposeValue = formData?.get("purpose");
1003+
const purpose = typeof purposeValue === "string" ? purposeValue : null;
10041004
const filename = `${uploadPrefix(purpose)}/${uuidv4()}${extensionForContentType(file.type)}`;
10051005
await createOssClient().put(filename, Buffer.from(await file.arrayBuffer()), {
10061006
headers: { "Content-Type": file.type },

‎apps/api/tsconfig.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"extends": "../../tsconfig.base.json",
33
"compilerOptions": {
44
"jsx": "react-jsx",
5+
"rootDir": "../..",
56
"outDir": "./dist",
67
"types": ["node"],
78
"noImplicitAny": false,

‎apps/web/app/components/CommandPalette.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ export function CommandPalette({
7272
const value = query.trim();
7373
if (!value) return;
7474
onOpenChange(false);
75-
navigate(`/search?q=${encodeURIComponent(value)}`);
75+
void navigate(`/search?q=${encodeURIComponent(value)}`);
7676
}
7777

7878
function go(to: string) {
7979
onOpenChange(false);
80-
navigate(to);
80+
void navigate(to);
8181
}
8282

8383
return (

‎apps/web/app/components/ThemeToggle.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function ThemeToggle() {
99
const applyToDocument = useThemeStore((s) => s.applyToDocument);
1010

1111
useEffect(() => {
12-
Promise.resolve(useThemeStore.persist.rehydrate()).then(() => {
12+
void Promise.resolve(useThemeStore.persist.rehydrate()).then(() => {
1313
useThemeStore.getState().applyToDocument();
1414
});
1515
}, [applyToDocument]);

‎apps/web/app/lib/stores/auth-store.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const useAuthStore = create<AuthState>()((set, get) => ({
3131
if (get().hydrated) return;
3232
set({ user, hydrated: true });
3333
if (user) {
34-
get().fetchUnread();
34+
void get().fetchUnread();
3535
}
3636
},
3737
fetchUnread: async () => {

‎apps/web/app/routes/_index.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export default function Index({ loaderData }: Route.ComponentProps) {
101101
onValueChange={(value) => {
102102
const params = new URLSearchParams();
103103
if (value !== "all") params.set("tab", value);
104-
navigate(params.size ? `/?${params.toString()}` : "/");
104+
void navigate(params.size ? `/?${params.toString()}` : "/");
105105
}}
106106
>
107107
<Card>

0 commit comments

Comments
 (0)