microsoft / microsoft/TypeScript
Design Meeting Notes, 2026-07-21
还没有人认领这个 Issue。
- 主要语言
- Go
- 星标
- 111k
- 派生
- 14.3k
- 平均合并
- 2 天 4 小时
- 30 天内合并 PR
- 132
描述
Strict nil Checking
-
Lots of issues you can see from the new codebase by searching the repo for "nil dereference".
-
Tools exist for static analysis in Go like nilaway
-
However, we ended up with over 1000 errors which seemed like too many to address.
-
One of the issues with nilaway is that it doesn't have a way of encoding invariants in the type system.
-
For example:
func IsParenthesizedTypeNode(node *Node) bool { return node.Kind == SyntaxKind.ParenthesizedType } func SkipTypeParentheses(node *Node) *Node { for IsParenthesizedTypeNode(node) { node = node.Type() } return node } // Acts as a helper to just grab the `Type` property // from various node types. func (n *Node) Type() *Node { switch n.Kind { case SyntaxKind.ParenthesizedType: return n.AsParenthesizedType().Type // ... } -
In that example, nilaway complains that
nodemay be nil in the call tonode.Type(), but we know that it can't be because of the invariant established byIsParenthesizedTypeNode.- Back in TypeScript, we would just grab the property
node.typebecauseIsParenthesizedTypeNodewould have been a type guard that would narrow the type ofnodetoParenthesizedTypeNode. - Now we have a virtual call and nilaway can't link whatever we've learned from the call to
IsParenthesizedTypeNode.
- Back in TypeScript, we would just grab the property
-
-
So @gabritto has been prototyping a linting pass that is able to encode much of the same information from TypeScript's control flow analysis and type guards over our Go codebase.
-
[[ Example of it catching issues like https://github.com/microsoft/typescript-go/issues/1948 ]]
-
Uses comment suffixes.
// Type alias that is purely for documentation purposes. type CallExpressionNode = Node //ref: struct { Kind KindCallExpression; data DefPtr[CallExpression] } // DefCallExpressionNode is a non-nilable pointer to a CallExpressionNode. type DefCallExpressionNode = *CallExpressionNode //ref: nonnil // A type guard: //ref: node is DefCallExpressionNode func IsCallExpression(node DefNode) bool { return node.Kind == SyntaxKind.CallExpression } // A union type! type NodeWithText = Node //ref: IdentifierNode | NumberLiteralNode type DefNodeWithText = *NodeWithText //ref: nonnil -
Could we try to make the default non-nilable?
- Would probably make a lot of stuff less-specially-annotated.
- Would we be doing this checking on all types or just some subset of ours?
- All right now.
-
Does this work well given that these are just aliases in Go?
- Go is good at preserving type aliases.
-
How does this system know that something is a "discriminated union"?
- It's similar to what we do in TypeScript.
- We basically look for specific literal values in a common property across the set of types.
-
Basically the comment format uses Go syntax for structs along with new syntax for unions.
-
Looks extremely promising so far.
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
未指定文件、测试或具体入口点。首先查看严格 nil 检查的讨论以及原型的注释后缀格式;要视为完成,需要确定 linting 遍历的范围、其 nilability 默认值,以及它对 type guards 和判别联合的处理方式。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- go
- 领域
- compilers
- Issue 类型
- 功能
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 冷清
- 描述清晰度
- 需要澄清
- 新手友好度
- 30/100