# 4.5.3-2c Underlying Type

[接口](/golang-bian-yi-qi-lei-xing-jian-cha/4.4.42-lei-xing-shu-ju-jie-gou-jie-kou.md)的申明中包含`Underlying() Type`这个方法，而所有的类型中只有[Named 类型](/golang-bian-yi-qi-lei-xing-jian-cha/4.4.47-named-lei-xing-fl.md)的 Underlying Type 与自身不一样，其余所有类型的该方法都是返回自身。

在[类型表达式的类型检查](/golang-bian-yi-qi-lei-xing-jian-cha/4.5.31.3b-lei-xing-biao-da-shi-de-lei-xing-jian-cha.md)中我们知道编译器在做类型检查时，所有的类型信息都是通过函数`check.typ()`创建的，那么程序中的哪些表达式会导致调用该函数呢？答案是真正声明了一个类型的表达式，下列例子都会申明一个类型：

```go
int // 申明一个 int 类型
map[string]bool // 申明一个 map 类型
<- chan int // 申明一个 channel 类型
struct {/* 忽略 Fields */} // 申明一个 struct 类型
interface { /* 忽略方法申明 */ } // 申明一个 interface 类型
```

上面的每一行都会促使编译器创建一个类型，这种表达式叫着类型字面量（Type Literal），在[类型的等价规则](/golang-bian-yi-qi-lei-xing-jian-cha/4.4.412-lei-xing-de-deng-jia-gui-ze-fl.md)中我们提到 Go 采用的是结构化类型系统，只要类型的结构一致，那么就是等价的，所以下列代码是合法的：

```go
func f(s struct {
	name string
	age  int
})

func main() {
	f(struct {
		name string
		age  int
	}{"golang", 12})
}
```

如果只能够通过字面量的方式创建类型，那就太繁琐了：在每个需要类型的地方都必须将类型的结构重新申明一遍。解决该问题的办法是为字面量类型取一个名字，该任务由`type`关键字完成。因此我们可以将所有`type`的申明分为三个部分： `type  <name> <type literal>`

* type: 关键字，提醒编译器接下来进入类型申明的代码块
* \<name>: 类型名字
* \<type literal>: 实际的类型值，该值将与 \<name> 绑定

这也是为何通过`type`关键字申明的类型叫`Named`的原因。而 \<type literal> 所代表的实际类型，就是`Named`的 Underlying Type.

有了对 Named 类型与 Underlying Type 的了解，我们就可以对与类型相关的很多行为有更深入的理解了。[Go Spec](https://golang.org/ref/spec#Types)中将类型概述为 "A type determines a set of values together with operations and methods specific to those values", 简单来说就是值与方法的集合。对于 Named 类型而言，通过类型结构可以发现：值保存在 Underly Type 中，而方法保存在Named 类型之中。所以在形如`X.sel`的表达式，在不考虑内嵌属性的情况下，如果 sel 是属性，则会从 Underlying Type 中查找，而如果 sel 是方法，则与 Underlying Type 无关。参见如下代码：

```go
type A struct {
	name string
}

func (this *A) getName() string {
	return "A: " + this.name
}

type B A

// 可以为类型 B 定义与类型 A 一样的方法
func (this *B) getName() string {
	return "B: " + this.name
}

type C B

func TestABC(t *testing.T) {
	var c C = C{name: "golang"} // OK, C 的 underlying type 是 A, 也有 name 属性

	_ = c.name      // 可以引用 name 属性
	_ = c.getName() // Compile error: c.getName undefined (type C has no field or method getName) [MissingFieldOrMethod]
}
```

上述代码中，类型 A, B, C 的 Underlying Type 都是`struct { name string }`, 对应[Struct 类型](/golang-bian-yi-qi-lei-xing-jian-cha/4.4.45-struct-lei-xing.md), 所以类型 C 引用属性 name 没有问题。而方法`getName()`定义在类型 A 上，所以通过类型 C 无法引用。对于申明`type name decl`, name 的 Underlying Type 确定逻辑如下：

```go
func under(t Type) Type {
	if n := asNamed(t); n != nil {
		return n.under()
	}
	return t
}
```

其中参数 t 是 decl 的类型，即如果 decl 是 Named 类型，则递归查找其 Underlying Type, 直到遇到非 Named 类型为止，则该类型就是整个链上所有 Named 类型的 Underlying Type.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gocompiler.shizhz.me/golang-bian-yi-qi-lei-xing-jian-cha/4.5.3-2c-underlying-type.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
