npx skills add https://github.com/cxuu/golang-skills --skill go-interfacesscripts/check-interface-compliance.sh — 查找缺少编译时合规性检查(var _ I = (*T)(nil))的导出接口。运行 bash scripts/check-interface-compliance.sh --help 查看选项。接口属于消费值的包,而不是实现它们的包。从构造函数返回具体(通常是指针或结构体)类型,以便可以在不重构的情况下添加新方法。
// 良好:消费者定义其所需的接口
package consumer
type Thinger interface { Thing() bool }
func Foo(t Thinger) string { ... }
// 良好:生产者返回具体类型
package producer
type Thinger struct{ ... }
func (t Thinger) Thing() bool { ... }
func NewThinger() Thinger { return Thinger{ ... } }
// 不佳:生产者定义并返回自己的接口
package producer
type Thinger interface { Thing() bool }
type defaultThinger struct{ ... }
func NewThinger() Thinger { return defaultThinger{ ... } }
不要在接口被使用之前定义它们。 如果没有实际的使用示例,很难判断接口是否真的必要。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
如果一个类型的存在仅仅是为了实现一个接口,并且除了该接口之外没有导出任何其他方法,那么从构造函数返回接口以隐藏实现:
func NewHash() hash.Hash32 {
return &myHash{} // 未导出的类型
}
好处:实现可以更改而不影响调用者,替换算法只需更改构造函数调用。
如果不进行检查,失败的断言会导致运行时恐慌。始终使用逗号-ok 惯用法进行安全测试:
str, ok := value.(string)
if ok {
fmt.Printf("字符串值是:%q\n", str)
}
检查一个值是否实现了某个接口:
if _, ok := val.(json.Marshaler); ok {
fmt.Printf("值 %v 实现了 json.Marshaler\n", val)
}
重用变量名(t := t.(type))是惯用的做法 —— 该变量在每个 case 分支中都具有正确的类型。当一个 case 列出多个类型时(case int, int64:),变量具有接口类型。
避免在公共结构体中嵌入类型 —— 内部类型的完整方法集会成为你公共 API 的一部分。请改用未导出的字段。
当使用结构体嵌入进行组合、重写嵌入方法、解决命名冲突、应用 HandlerFunc 适配器模式或决定是否在公共 API 类型中嵌入时,请阅读 references/EMBEDDING.md。
使用空白标识符赋值在编译时验证类型是否实现了接口:
var _ json.Marshaler = (*RawMessage)(nil)
如果 *RawMessage 没有实现 json.Marshaler,这将导致编译错误。
在以下情况下使用此模式:
不要为每个接口都添加这些检查 —— 仅在没有其他静态转换能捕获错误时使用。
验证:定义接口或实现后,运行
bash scripts/check-interface-compliance.sh以验证所有具体类型都有编译时var _ I = (*T)(nil)检查。
如有疑问,请使用指针接收器。不要在同一类型上混合使用接收器类型 —— 如果任何方法需要指针,则所有方法都使用指针。仅对小的、不可变的类型(Point、time.Time)或基本类型使用值接收器。
在为新类型(特别是包含同步原语或大型结构体的类型)决定使用指针接收器还是值接收器时,请阅读 references/RECEIVER-TYPE.md。
| 概念 | 模式 | 备注 |
|---|---|---|
| 消费者拥有接口 | 在使用处定义接口 | 不在实现包中 |
| 安全类型断言 | v, ok := x.(Type) | 返回零值 + false |
| 类型选择 | switch v := x.(type) | 变量在每个 case 中具有正确类型 |
| 接口嵌入 | type RW interface { Reader; Writer } | 方法的并集 |
| 结构体嵌入 | type S struct { *T } | 提升 T 的方法 |
| 接口检查 | var _ I = (*T)(nil) | 编译时验证 |
| 通用性 | 从构造函数返回接口 | 隐藏实现 |
-er 后缀约定)或选择接收器名称时,请参阅 go-namingerror 接口、自定义错误类型或 errors.As 匹配时,请参阅 go-error-handlingvar _ I = (*T)(nil) 满足性检查时,请参阅 go-defensive每周安装数
239
仓库
GitHub 星标数
56
首次出现
2026年1月27日
安全审计
安装于
github-copilot221
opencode213
gemini-cli211
codex211
kimi-cli208
amp208
scripts/check-interface-compliance.sh — Finds exported interfaces missing compile-time compliance checks (var _ I = (*T)(nil)). Run bash scripts/check-interface-compliance.sh --help for options.Interfaces belong in the package that consumes values, not the package that implements them. Return concrete (usually pointer or struct) types from constructors so new methods can be added without refactoring.
// Good: consumer defines the interface it needs
package consumer
type Thinger interface { Thing() bool }
func Foo(t Thinger) string { ... }
// Good: producer returns concrete type
package producer
type Thinger struct{ ... }
func (t Thinger) Thing() bool { ... }
func NewThinger() Thinger { return Thinger{ ... } }
// Bad: producer defines and returns its own interface
package producer
type Thinger interface { Thing() bool }
type defaultThinger struct{ ... }
func NewThinger() Thinger { return defaultThinger{ ... } }
Do not define interfaces before they are used. Without a realistic example of usage, it is too difficult to see whether an interface is even necessary.
If a type exists only to implement an interface with no exported methods beyond that interface, return the interface from constructors to hide the implementation:
func NewHash() hash.Hash32 {
return &myHash{} // unexported type
}
Benefits: implementation can change without affecting callers, substituting algorithms requires only changing the constructor call.
Without checking, a failed assertion causes a runtime panic. Always use the comma-ok idiom to test safely:
str, ok := value.(string)
if ok {
fmt.Printf("string value is: %q\n", str)
}
To check if a value implements an interface:
if _, ok := val.(json.Marshaler); ok {
fmt.Printf("value %v implements json.Marshaler\n", val)
}
It's idiomatic to reuse the variable name (t := t.(type)) — the variable has the correct type in each case branch. When a case lists multiple types (case int, int64:), the variable has the interface type.
Avoid embedding types in public structs — the inner type's full method set becomes part of your public API. Use unexported fields instead.
Read references/EMBEDDING.md when using struct embedding for composition, overriding embedded methods, resolving name conflicts, applying the HandlerFunc adapter pattern, or deciding whether to embed in public API types.
Use a blank identifier assignment to verify a type implements an interface at compile time:
var _ json.Marshaler = (*RawMessage)(nil)
This causes a compile error if *RawMessage doesn't implement json.Marshaler.
Use this pattern when:
Don't add these checks for every interface — only when no other static conversion would catch the error.
Validation : After defining interfaces or implementations, run
bash scripts/check-interface-compliance.shto verify all concrete types have compile-timevar _ I = (*T)(nil)checks.
If in doubt, use a pointer receiver. Don't mix receiver types on a single type — if any method needs a pointer, use pointers for all methods. Use value receivers only for small, immutable types (Point, time.Time) or basic types.
Read references/RECEIVER-TYPE.md when deciding between pointer and value receivers for a new type, especially for types with sync primitives or large structs.
| Concept | Pattern | Notes |
|---|---|---|
| Consumer owns interface | Define interfaces where used | Not in the implementing package |
| Safe type assertion | v, ok := x.(Type) | Returns zero value + false |
| Type switch | switch v := x.(type) | Variable has correct type per case |
| Interface embedding | type RW interface { Reader; Writer } | Union of methods |
| Struct embedding | type S struct { *T } | Promotes T's methods |
-er suffix convention) or choosing receiver nameserror interface, custom error types, or errors.As matchingvar _ I = (*T)(nil) satisfaction checks at API boundariesWeekly Installs
239
Repository
GitHub Stars
56
First Seen
Jan 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot221
opencode213
gemini-cli211
codex211
kimi-cli208
amp208
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
| Interface check |
var _ I = (*T)(nil) |
| Compile-time verification |
| Generality | Return interface from constructor | Hide implementation |