如何创建强类型且禁止引用不存在属性的TypeScript对象?
嘿,这个需求在TypeScript里实现起来非常顺畅,刚好匹配强类型语言的优势!我给你分享两种实用方案,看哪种更贴合你的场景:
实现强类型查询对象的两种方案
方案1:利用as const和类型推导(最简快捷)
如果你不想手动写接口,直接用const断言就能让TypeScript自动推导出精确的键类型,完美实现编译时的属性检查:
// queries.ts export const queries = { getUser: 'SELECT * FROM users WHERE id = ?', getPosts: 'SELECT * FROM posts WHERE user_id = ?' } as const; // 在其他文件导入使用 import { queries } from './queries'; // ✅ 完全合法,编译器能精准识别getUser是有效键 const userQuery = queries.getUser; // ❌ 编译直接报错!提示“类型‘typeof queries’上不存在属性‘getComments’” const invalidQuery = queries.getComments;
为什么这能生效? as const会让TypeScript把对象的每个属性都锁定为字面量类型,同时把整个对象标记为只读。这样编译器就能严格校验你引用的键是否存在,完全符合你要的“禁止引用不存在属性”的要求。
方案2:手动定义接口+satisfies约束(适合复用类型场景)
如果需要把查询的类型单独抽出来(比如在函数参数、返回值里复用),可以先定义接口,再用satisfies关键字约束对象结构:
// 先明确所有合法的查询键和对应类型 interface QueryMap { getUser: string; getPosts: string; } // 用`satisfies`保证对象符合接口,同时保留自身的字面量类型细节 export const queries = { getUser: 'SELECT * FROM users WHERE id = ?', getPosts: 'SELECT * FROM posts WHERE user_id = ?' } satisfies QueryMap;
小提示:如果用const queries: QueryMap = { ... }的方式,变量类型会被拓宽为接口类型,丢失字面量信息;而satisfies既能保证结构合规,又能保留更精确的类型,是TypeScript 4.9+版本的推荐写法。
额外加固:禁止运行时修改对象
如果还想防止在运行时给queries添加或修改属性,可以结合Object.freeze:
export const queries = Object.freeze({ getUser: 'SELECT * FROM users WHERE id = ?', getPosts: 'SELECT * FROM posts WHERE user_id = ?' } as const); // ❌ 编译时报错,严格模式下运行时也会报错 queries.getComments = 'SELECT * FROM comments WHERE user_id = ?';
这样从编译到运行阶段,都能彻底保证对象结构的稳定性。
内容的提问来源于stack exchange,提问作者Nathan Friend
相关产品推荐
相关产品推荐

