TypeScript无法识别typeRoots配置目录下的全局自定义类型(如User)
看起来你踩了TypeScript typeRoots 配置的两个常见误区,我来帮你拆解问题并给出可行的解决办法:
问题根源分析
1. typeRoots 的作用被误解
typeRoots 并不是用来自动加载任意目录下的模块类型的——它的设计目标是指定全局类型声明文件(没有export/import的.d.ts)或者**@types风格的类型包目录**(每个类型是一个子目录,比如@types/react这种结构)。
你的types.d.ts里用了export type User = ...,这会让文件变成一个模块,而不是全局类型声明。这种情况下,不管typeRoots怎么配置,TypeScript都不会自动把模块里的类型变成全局可用的。
2. 即使目录被扫描,模块类型也不会自动导入
从你的--traceResolution输出能看到TS确实在检查src/generated目录,但因为这个文件是模块,TS不会主动导入它的内容——你必须手动导入,或者把它改成全局声明。
可行的解决方案
方案1:手动导入类型(最推荐,符合模块规范)
既然types.d.ts是模块,直接在需要用User的文件里手动导入即可:
// src/util/userUtil.ts import type { User } from '../generated/types'; export const getUser = (id: string): User => { // 你的代码逻辑 };
这种方式最清晰,也符合TypeScript的模块设计理念,不需要依赖typeRoots的配置。
方案2:将类型改为全局可用(适合确实需要全局的场景)
如果你希望User等类型不需要导入就能全局使用,可以修改src/generated/types.d.ts,把类型放到全局命名空间中:
// src/generated/types.d.ts declare global { type User = Node & { /* 你的类型定义内容 */ }; // 其他需要全局的类型也放在这里 } // 必须加这一行,确保文件被识别为模块,让`declare global`生效 export {};
然后确保tsconfig.json的include包含了这个文件(你说已经包含了,所以没问题),之后User类型就会全局可用,不需要导入。
方案3:调整目录结构适配typeRoots(不推荐,繁琐)
如果你一定要用typeRoots来加载类型,需要把目录改成TS期望的@types风格结构:
- 新建目录:
src/generated/@types/my-graphql-types - 把
types.d.ts的内容移到这个目录下的index.d.ts(注意去掉export,变成全局类型) - 修改
tsconfig.json的typeRoots:"typeRoots": ["./src/generated/@types", "./node_modules/@types"]
这种方式比较繁琐,除非你有大量全局类型包需要管理,否则不推荐使用。
最后再确认下:你的skipLibCheck和include配置没问题,核心问题就是typeRoots的使用场景和模块类型的加载逻辑不匹配,选上面任意一个方案都能解决你的报错。
内容来源于stack exchange

