Go types.Info.TypeOf 怎样读取表达式类型
在做 Go 源码分析时,拿到一个 ast.Expr 只是看到了语法节点,还不知道它代表 int、结构体还是函数签名。正确做法是先让 go/types 完成类型检查,再用 types.Info.TypeOf(expr) 查询该表达式的静态类型。
官方文档:https://pkg.go.dev/go/types#Info.TypeOf
Info.TypeOf返回表达式的类型;如果表达式没有被记录则返回nil。它的前提是Types、Uses和Defs映射已经由类型检查过程填充。
先把类型检查结果准备好
TypeOf 不是独立的 AST 推断器,它读取的是 Info 中已经生成的结果。因此要初始化需要的映射,并把同一个 Info 传给 Config.Check。下面的示例从内存源码解析包,再定位函数返回语句里的二元表达式。
package main
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"strings"
)
func main() {
// 保留源码位置,后面可用 token.Pos 回到文件和行列信息。
fset := token.NewFileSet()
src := `package demo
func Sum(a int, b int) int { return a + b }`
file, err := parser.ParseFile(fset, "demo.go", strings.NewReader(src), 0)
if err != nil {
panic(err)
}
// 只初始化要查询的映射;TypeOf 的前提由这些映射提供。
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{Importer: importer.Default()}
if _, err := conf.Check("demo", fset, []*ast.File{file}, info); err != nil {
panic(err)
}
// 遍历只负责找到表达式,类型结论交给 Info.TypeOf。
ast.Inspect(file, func(node ast.Node) bool {
binary, ok := node.(*ast.BinaryExpr)
if !ok {
return true
}
typ := info.TypeOf(binary)
fmt.Println("表达式类型:", typ.String())
return false
})
}
这里的关键关系是:parser.ParseFile 产生语法树,Config.Check 填充 Info,TypeOf 只查询指定表达式的结果。不要把 ast.Inspect 找到节点误认为已经完成了类型推断。

用 TypeOf 查询表达式的静态类型
调用方式很直接:把目标表达式节点传入 info.TypeOf(expr),返回值是 types.Type 接口。需要展示类型时可以调用 String();需要继续判断底层类型时,再根据业务使用 Underlying() 或类型断言。
func expressionType(info *types.Info, expr ast.Expr) (string, bool) {
// TypeOf 返回 nil 代表该表达式没有可用的类型记录。
typ := info.TypeOf(expr)
if typ == nil {
return "", false
}
// String 是展示类型的便捷方式,不应拿字符串代替类型比较。
return typ.String(), true
}
在示例中,a + b 是函数返回表达式,经过完整检查后通常可以得到 int。如果查询的是常量表达式,返回类型也可能是未定类型,例如 untyped int;这说明表达式仍处在语言的常量类型语义中,不要只按最终赋值后的默认类型理解。
当需要比较两个类型是否相同,应使用 types.Identical 等类型 API;不要用 typ.String() 的文本相等替代类型身份判断。图中连线只表示数据职责和查询关系,不表示已经执行过某段代码。

为什么有些标识符查不到类型
Info.Types 并不记录每一个标识符。它主要记录可以作为任意表达式出现的 ast.Expr。官方文档特别区分了几类节点:
- 选择器
x.f中的字段标识符,相关信息通常在Selections中; var z int中声明变量的标识符,相关对象在Defs中;- 限定标识符
p.X中代表包名的标识符,相关对象在Uses中。
所以,遇到 TypeOf 返回 nil 时,先确认传入的是表达式节点,再确认对应的 Info 映射是否在检查前初始化。若目标是“这个名字绑定了什么对象”,应改用 ObjectOf 或直接读取 Defs、Uses,不要反复调用 TypeOf。
nil 和不完整检查要分开处理
TypeOf 的 nil 只说明当前表达式没有可用记录,可能是传入节点不在本次检查的文件集合中,也可能是该节点属于 Types 不记录的范围。另一方面,Config.Check 返回错误时,Info 中已经收集的结果可能不完整,不能把部分结果当成全包结论。
func safeTypeName(info *types.Info, expr ast.Expr) string {
// 先判空,避免对缺失记录直接调用 String 导致 panic。
typ := info.TypeOf(expr)
if typ == nil {
return "未记录表达式类型"
}
return typ.String()
}
实际工具可以同时保留 Check 返回的错误、文件位置和目标表达式文本:有错误时提示“类型信息可能不完整”,无错误时再把 TypeOf 结果作为稳定查询结果。这样既能处理正常源码分析,也能避免静态检查工具在损坏输入上给出过度确定的结论。
常见问题
TypeOf 返回 nil 是 API 调用失败吗?
不一定。它的语义是没有找到该表达式的类型记录,应检查节点类型、Info 映射和本次 Check 的文件范围。
可以用 TypeOf 查询变量声明的类型吗?
声明名通常应从 Defs 获取对象,再读取对象的 Type();不要假设声明标识符一定存在于 Types。
Check 返回错误后还可以读 TypeOf 吗?
可以读已经收集到的结果,但官方说明这些信息可能不完整。工具应保留错误并降低结论确定性。
总结:先用 Config.Check 填充 Info,再把真实的 ast.Expr 交给 TypeOf;得到 nil 时区分“未记录”和“检查不完整”,需要查标识符绑定时则切换到 Defs、Uses 或 Selections。
Go go/token.FileSet.File 怎样查询源码位置
- 上一篇
- Go go/token.FileSet.File 怎样查询源码位置
- 下一篇
- Go go/types.Info.ObjectOf 怎样找到标识符对应对象
-
- Golang · Go问答 | 24分钟前 |
- Go simd/archsimd 为什么代码不能跨架构(参数形态)
- 205浏览 收藏
-
- Golang · Go问答 | 26分钟前 |
- Go runtime/debug.SetCrashOutput 怎样把崩溃输出转到文件
- 431浏览 收藏
-
- Golang · Go问答 | 40分钟前 |
- Go ResponseController.EnableFullDuplex 怎样让服务端边读边写
- 436浏览 收藏
-
- Golang · Go问答 | 43分钟前 |
- Go net.DNSError Timeout 怎样判断 DNS 查询是否真的超时
- 296浏览 收藏
-
- Golang · Go问答 | 51分钟前 |
- Go simd 为什么实验类型无法编译(参数形态)
- 377浏览 收藏
-
- Golang · Go问答 | 56分钟前 |
- Go goavro 解码后为什么还返回未消费的字节
- 449浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go go/token.FileSet.File 怎样查询源码位置
- 233浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go uuid 为什么导入路径找不到(参数形态)
- 234浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go jsontext Decoder PeekKind 怎样判断下一个 token 类型
- 474浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go jsontext Decoder ReadValue 为什么在结束标记处返回错误
- 399浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go jsontext Token Clone 怎样保留跨次读取的原始 token
- 402浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 679次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 696次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 741次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 711次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 548次使用
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- Go JSONJSON null 解码到指针后状态不符合预期怎么办
- 2026-10-18 501浏览
-
- Go crypto/rand.Text 的长度为什么不是固定字符数
- 2026-10-04 501浏览
-
- Go strings.ToValidUTF8 清洗日志内容的边界
- 2026-10-03 501浏览

