mcpField
C
consistent-aliases

consistent-aliases

github.com/marius-townhouse开发643 次安装38 周前更新
代码仓库

关于 consistent-aliases

介绍如何一致地使用别名,使 TypeScript 控制流收窄符合预期。指南说明别名与源属性会被分别跟踪,并提供解构、局部变量和 readonly 参数等建议。

主要功能

  1. 01一致使用别名或源属性
  2. 02通过解构保持命名一致
  3. 03函数调用可能改值时优先使用局部变量
  4. 04用 readonly 参数保护类型收窄
  5. 05为 Map.get 和回退值采用安全写法

适用场景

  1. 01排查意外的 TypeScript 类型收窄错误
  2. 02避免属性检查后出现 undefined
  3. 03审查混用别名与属性访问的代码

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill consistent-aliases

安装后在 Claude Code 中输入/consistent-aliases确认已加载。

详细介绍

始终一致地使用别名

概述

如果创建了别名,就要始终一致地使用它。

将对象属性赋给变量时,就创建了一个别名。TypeScript 会分别跟踪它们,因此缩窄其中一个不会缩窄另一个。选定一个并始终使用它。

何时使用此技能

  • 类型缩窄没有按预期生效
  • 同时使用变量及其源属性
  • 属性检查没有影响别名变量
  • 函数调用后,类型细化“丢失”

铁律

One alias OR the original - never mix them in the same scope.
Prefer destructuring for consistent naming.

请记住:

  • 别名和原始变量会被分别跟踪
  • 缩窄其中一个不会缩窄另一个
  • 函数调用可能会使属性类型细化失效
  • 局部变量比对象属性更安全

检测:别名会破坏类型缩窄

interface Polygon {
  exterior: Coordinate[];
  bbox?: BoundingBox;
}

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  const box = polygon.bbox;  // Created an alias
  
  if (polygon.bbox) {  // Narrows polygon.bbox
    if (pt.x < box.x[0]) {  // box is still possibly undefined!
      //       ~~~
      // 'box' is possibly 'undefined'
    }
  }
}

对 polygon.bbox 的检查不会缩窄 box。

黄金法则:始终一致地使用别名

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  const box = polygon.bbox;
  
  if (box) {  // Check the alias
    if (pt.x < box.x[0]) {  // Use the alias
      // OK - box is narrowed here
    }
  }
}

现在,检查和使用都引用同一个变量。

最佳实践:使用解构

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  const { bbox } = polygon;  // Same name as property
  
  if (bbox) {
    const { x, y } = bbox;  // Continue destructuring
    if (pt.x < x[0] || pt.x > x[1] || pt.y < y[0] || pt.y > y[1]) {
      return false;
    }
  }
  return true;
}

优点:

  • 命名一致(不会混淆 box 和 bbox)
  • 更简洁
  • 与 TypeScript 的控制流分析配合良好

别名也会影响运行时

别名会影响运行时行为:

const { bbox } = polygon;

if (!bbox) {
  calculatePolygonBbox(polygon);  // Fills in polygon.bbox
  // Now polygon.bbox exists, but bbox is still undefined!
}

别名和原始变量在运行时可能会出现不一致。

函数调用与类型细化

TypeScript 对函数调用采取务实的处理方式:

function expandPolygon(p: Polygon) { /* ... */ }

if (polygon.bbox) {
  polygon.bbox  // BoundingBox (narrowed)
  
  expandPolygon(polygon);
  
  polygon.bbox  // Still BoundingBox (TypeScript trusts you)
  // But the function might have set it to undefined!
}

TypeScript 假设函数不会使类型细化失效。这通常没问题,但也可能出错。

更安全的做法:使用局部变量

if (polygon.bbox) {
  const bbox = polygon.bbox;  // Capture it locally
  
  expandPolygon(polygon);
  
  bbox  // Still BoundingBox - local variable is safe
}

函数调用无法修改局部变量。

使用 Readonly 提高安全性

function safeExpand(p: Readonly<Polygon>) {
  // Can't modify p.bbox
}

if (polygon.bbox) {
  safeExpand(polygon);
  polygon.bbox  // Guaranteed still BoundingBox
}

Readonly 参数可以避免对可变性的顾虑。

常见模式

使用空值合并,而非别名
// Instead of:
const name = person.nickname;
const displayName = name ? name : person.fullName;

// Use:
const displayName = person.nickname ?? person.fullName;
Map.get 模式
// TypeScript doesn't connect has() and get():
if (map.has(key)) {
  const value = map.get(key);  // Still T | undefined
}

// Better pattern:
const value = map.get(key);
if (value !== undefined) {
  value  // T (narrowed)
}

// Or with nullish coalescing:
const value = map.get(key) ?? defaultValue;

抗压应对准则

1. “我需要使用两个名称”

压力:“有时我用属性,有时我用变量”

**回应:**选一个。混用会破坏类型缩窄。

**行动:**使用解构,保持命名一致。

2. “函数不会修改它”

压力:“我知道 expandPolygon 不会碰 bbox”

**回应:**TypeScript 无法确定这一点。用 Readonly 明确说明。

**行动:**使用局部变量或 readonly 参数来确保安全。

危险信号——停下来重新考虑

  • 根据属性创建变量后,又在条件中使用该属性
  • if 检查后仍出现“可能是 undefined”的类型错误
  • 在同一代码块中混用属性访问和变量
  • 依赖函数调用后的类型细化

常见的自我开脱(都不成立)

借口事实
“别名就是同一个东西”TypeScript 会分别跟踪它们
“我已经检查过这个属性了”检查不会缩窄别名
“函数不会修改它”TypeScript 无法验证这一点

快速参考

// DON'T: Mix alias and original
const box = polygon.bbox;
if (polygon.bbox) {  // Checks original
  box.x;  // Uses alias - still possibly undefined!
}

// DO: Use alias consistently
const box = polygon.bbox;
if (box) {  // Checks alias
  box.x;  // Uses alias - narrowed correctly
}

// DO: Use destructuring
const { bbox } = polygon;
if (bbox) {
  const { x, y } = bbox;
}

要点

选定一个名称,并始终一致地使用它。

为属性创建别名时,TypeScript 会分别跟踪别名和原始变量。缩窄其中一个不会缩窄另一个。使用解构来保持命名一致;如果函数调用可能使类型细化失效,则优先使用局部变量。

参考资料

基于 Dan Vanderkam 所著的《Effective TypeScript》第 23 条:始终一致地使用别名。

评论

支持 Markdown