如何配置TypeScript/ESLint在knex返回any类型时强制报错?
以下方案组合使用可以完全杜绝knex查询隐式返回any的问题,不需要额外引入重型工具:
首先开启TypeScript+ESLint的全严格校验规则,从编译、lint层阻断隐式
any流转
在tsconfig.json中开启核心严格配置:{ "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true } }配合ESLint的TypeScript规则集开启三个核心校验:
@typescript-eslint/no-unsafe-assignment、@typescript-eslint/no-unsafe-call、@typescript-eslint/no-unsafe-member-access。配置完成后,只要knex查询返回了any类型,后续对返回值的赋值、方法调用、属性读取操作都会直接报错,根本无法通过本地校验和流水线卡点。初始化knex时全局注册表结构类型映射,从根源避免漏标类型
不要每次查询单独传入泛型,通过knex内置的类型扩展接口,把项目里所有数据表的结构统一注册,之后所有常规查询都会自动推导返回类型,未注册的表名会直接抛类型错误,不会回退到any:import { Knex, knex } from 'knex'; // 提前定义各表的字段类型 interface User { id: number; username: string; createTime: Date; } interface Order { id: number; userId: number; payAmount: number; } // 扩展knex内置的Tables类型,绑定表名和对应结构 declare module 'knex/types/tables' { interface Tables { users: Knex.CompositeTableType< User, // 插入操作的字段类型:id自增、createTime可自动生成,只要求必传username Pick<User, 'username'> & Partial<Pick<User, 'createTime'>>, // 更新操作的字段类型:不允许修改id,其余字段可选 Partial<Omit<User, 'id'>> >; orders: Order; } } // 初始化的实例所有查询自动匹配类型,不需要手动传泛型 const db = knex({ /* 数据库连接配置 */ }); // 这里返回值自动推导为User[],不会出现any const userList = await db('users').select('*');如果写了未在
Tables中注册的表名,TS会直接抛出类型错误,从入口就避免漏配表结构的问题。封装通用查询方法,强制要求传入明确类型
如果项目里存在大量复杂联表查询、内置类型推导覆盖不到的场景,可以封一层通用查询函数,把返回类型设为必须传入的泛型参数,不传的话返回值会被推导为never,使用时直接报错:async function strictQuery<TResult = never>( buildQuery: (db: Knex) => Knex.QueryBuilder ): Promise<TResult[]> { const result = await buildQuery(db); return result as TResult[]; } // 漏传泛型的话返回值是never,根本无法正常使用 // 正确写法必须明确标注返回类型 const orderList = await strictQuery<{id: number; username: string; payAmount: number}>( (k) => k('users').leftJoin('orders', 'users.id', 'orders.userId').select('*') );
注意:不要为了过校验随便给查询结果标
any,只要上述配置全开,出现any就意味着要么表结构没注册、要么查询逻辑存在类型漏洞,顺着报错补齐类型定义即可,完全可以做到全链路无隐式any。
内容的提问来源于stack exchange,提问作者JonathanR

