<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://www.devng.cn/</id>
  <title type="text">CyreneStar</title>
  <subtitle type="text">技术探索与工程实践</subtitle>
  <updated>2026-08-09T00:00:00.000Z</updated>
  <author><name>CyreneStar</name></author>
  <link rel="alternate" href="https://www.devng.cn/"/>
  <link rel="self" href="https://www.devng.cn/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://www.devng.cn/posts/git-conventional-commits/</id>
      <title type="text">Git 提交规范与 Conventional Commits 实践</title>
      <published>2026-08-09T00:00:00.000Z</published>
      <updated>2026-08-09T00:00:00.000Z</updated>
      <author><name>CyreneStar</name></author>
      <link rel="alternate" href="https://www.devng.cn/posts/git-conventional-commits/"/>
      <summary type="text">为什么需要提交规范、Conventional Commits 规范详解、如何配合 git hooks 与工具自动校验，以及语义化版本号如何从中推导。</summary>
      <content type="html"><![CDATA[<section><h2>为什么需要提交规范<a href="#为什么需要提交规范"><span>#</span></a></h2><p>团队提交历史常常变成一团乱麻：<code>fix bug</code>、<code>update</code>、<code>改了一下</code>。这类信息对半年后的你毫无价值。好的提交信息应当回答两个问题：</p><ol>
<li><strong>改了什么</strong>（what）</li>
<li><strong>为什么改</strong>（why）</li>
</ol><p>提交规范的价值在于：机器可解析、人类可读、能自动生成 CHANGELOG、能推导语义化版本号。</p></section>
<section><h2>Conventional Commits 规范<a href="#conventional-commits-规范"><span>#</span></a></h2><p>格式如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;type&gt;(&lt;scope&gt;): &lt;subject&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>&lt;body&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>&lt;footer&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><code>type</code>：提交类型（见下表）</li>
<li><code>scope</code>：可选，影响范围（模块/组件）</li>
<li><code>subject</code>：简短描述，祈使句、不加句号、不超过 50 字</li>
<li><code>body</code> / <code>footer</code>：可选，说明动机与破坏性变更</li>
</ul><section><h3>常用 type<a href="#常用-type"><span>#</span></a></h3>

<table><thead><tr><th>type</th><th>含义</th></tr></thead><tbody><tr><td>feat</td><td>新功能</td></tr><tr><td>fix</td><td>修复 bug</td></tr><tr><td>docs</td><td>文档变更</td></tr><tr><td>style</td><td>格式（不影响逻辑）</td></tr><tr><td>refactor</td><td>重构</td></tr><tr><td>perf</td><td>性能优化</td></tr><tr><td>test</td><td>测试</td></tr><tr><td>chore</td><td>构建/依赖等杂项</td></tr><tr><td>revert</td><td>回滚</td></tr></tbody></table></section><section><h3>示例<a href="#示例"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>feat(auth): 支持 OAuth2 第三方登录</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>新增 Google / GitHub 登录入口，用户信息通过 JWT 下发。</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>BREAKING CHANGE: 旧的 session 鉴权接口已下线。</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>带 <code>BREAKING CHANGE:</code> 的提交会被工具识别为<strong>破坏性变更</strong>，发布时升主版本号。</p></section></section>
<section><h2>用 commitlint + husky 自动校验<a href="#用-commitlint--husky-自动校验"><span>#</span></a></h2><p>光靠自觉不够，用工具在提交前拦下来。先在项目里加 <code>commitlint.config.js</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>extends</span><span>:</span><span> [</span><span>'@commitlint/config-conventional'</span><span>],</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>再用 husky 挂一个 <code>commit-msg</code> 钩子：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npx</span><span> </span><span>husky</span><span> </span><span>add</span><span> </span><span>.husky/commit-msg</span><span> </span><span>"npx --no-install commitlint --edit </span><span>$1</span><span>"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>此后不符合规范的提交会被直接拒绝，并提示正确格式。</p></section>
<section><h2>从提交推导版本号<a href="#从提交推导版本号"><span>#</span></a></h2><p>有了结构化提交，可以用 <code>standard-version</code> 或 <code>semantic-release</code> 自动：</p><ul>
<li><code>fix</code> → 升 <strong>patch</strong>（1.0.0 → 1.0.1）</li>
<li><code>feat</code> → 升 <strong>minor</strong>（1.0.0 → 1.1.0）</li>
<li><code>BREAKING CHANGE</code> → 升 <strong>major</strong>（1.0.0 → 2.0.0）</li>
</ul><p>同时自动生成 CHANGELOG，无需手工维护。</p><blockquote><p>规范不是束缚，而是给未来的自己和协作者留的一份地图。</p></blockquote></section>]]></content>
    </entry>
    <entry>
      <id>https://www.devng.cn/posts/redis-cache-design/</id>
      <title type="text">Redis 缓存设计：穿透、击穿、雪崩与实战</title>
      <published>2026-08-08T00:00:00.000Z</published>
      <updated>2026-08-08T00:00:00.000Z</updated>
      <author><name>CyreneStar</name></author>
      <link rel="alternate" href="https://www.devng.cn/posts/redis-cache-design/"/>
      <summary type="text">缓存三大经典问题（穿透/击穿/雪崩）的成因与解法，结合布隆过滤器、互斥锁、随机过期时间等策略，附伪代码与 Redis 最佳实践。</summary>
      <content type="html"><![CDATA[<section><h2>缓存的基本读写<a href="#缓存的基本读写"><span>#</span></a></h2><p>最常见的「Cache Aside」模式：</p><ol>
<li>读：先查缓存，命中直接返回；未命中查库，写入缓存。</li>
<li>写：先更新数据库，再删除缓存（而非更新缓存）。</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>读: cache.get(k) → 命中返回; 未命中 db.query → cache.set(k, v, ttl)</span></div></div><div><div><div>2</div></div><div><span>写: db.update → cache.del(k)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>缓存穿透：查根本不存在的数据<a href="#缓存穿透查根本不存在的数据"><span>#</span></a></h2><p><strong>成因</strong>：请求的参数在数据库中不存在（如 <code>id = -1</code>），缓存和数据库都查不到，恶意流量直接打到 DB。</p><p><strong>解法一：缓存空值</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>v = cache.get(k)</span></div></div><div><div><div>2</div></div><div><span>if v == nil:</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>v = db.query(k)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>if v == nil:</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>cache.set(k, NULL, 60s)   # 缓存短时间空标记</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>return v</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解法二：布隆过滤器</strong>——在缓存前加一层 BloomFilter，不存在的 key 直接拒绝。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>if !bloom.mightContain(k): return nil   # 一定不存在</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>缓存击穿：热点 key 突然失效<a href="#缓存击穿热点-key-突然失效"><span>#</span></a></h2><p><strong>成因</strong>：某个超热点 key（如首页配置）过期瞬间，海量请求同时穿透到 DB。</p><p><strong>解法：互斥锁（singleflight）</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>v = cache.get(k)</span></div></div><div><div><div>2</div></div><div><span>if v == nil:</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>if lock.tryAcquire(k):        # 只有一个线程回源</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>v = db.query(k)</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>cache.set(k, v, ttl)</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>lock.release(k)</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>else:</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>sleep(50ms); retry()      # 其余线程等待并重试</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>另一种思路是<strong>逻辑过期</strong>：value 里带过期时间戳，异步刷新，读时不阻塞。</p></section>
<section><h2>缓存雪崩：大量 key 同时失效<a href="#缓存雪崩大量-key-同时失效"><span>#</span></a></h2><p><strong>成因</strong>：批量 key 设置了相同 TTL，同一时刻集体失效，DB 瞬间被打垮。</p><p><strong>解法：随机过期时间</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>ttl = base + random(0, jitter)    # 例如 3600 ± 600 秒</span></div></div><div><div><div>2</div></div><div><span>cache.set(k, v, ttl)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>配合<strong>多级缓存</strong>（本地 Caffeine + Redis）与<strong>限流降级</strong>，可进一步兜底。</p></section>
<section><h2>最佳实践小结<a href="#最佳实践小结"><span>#</span></a></h2><ul>
<li>给所有 key 设 TTL，避免无限堆积；</li>
<li>写操作删缓存而非更新缓存；</li>
<li>大 value 拆分，避免热 key 集中；</li>
<li>监控命中率，命中率低于 90% 要警惕。</li>
</ul><blockquote><p>缓存的本质是用空间换时间，代价是<strong>一致性</strong>。明确业务能接受的最终一致窗口，再决定策略。</p></blockquote></section>]]></content>
    </entry>
    <entry>
      <id>https://www.devng.cn/posts/github-actions-cicd/</id>
      <title type="text">用 GitHub Actions 搭建轻量 CI/CD 流水线</title>
      <published>2026-08-07T00:00:00.000Z</published>
      <updated>2026-08-07T00:00:00.000Z</updated>
      <author><name>CyreneStar</name></author>
      <link rel="alternate" href="https://www.devng.cn/posts/github-actions-cicd/"/>
      <summary type="text">从零配置 GitHub Actions，实现代码推送自动 lint、测试、构建与部署，覆盖 workflow 语法、缓存加速、密钥管理与多环境发布。</summary>
      <content type="html"><![CDATA[<section><h2>核心概念<a href="#核心概念"><span>#</span></a></h2><ul>
<li><strong>Workflow</strong>：一个自动化流程，定义在 <code>.github/workflows/*.yml</code>。</li>
<li><strong>Job</strong>：一组串行步骤，可并行多个 Job。</li>
<li><strong>Step</strong>：具体命令或 Action。</li>
<li><strong>Runner</strong>：执行环境（GitHub 托管的 ubuntu/macos 或自托管）。</li>
</ul></section>
<section><h2>一个 Node 项目的最小流水线<a href="#一个-node-项目的最小流水线"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>name</span><span>: </span><span>CI</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>on</span><span>:</span></div></div><div><div><div>4</div></div><div><span>  </span><span>push</span><span>:</span></div></div><div><div><div>5</div></div><div><span>    </span><span>branches</span><span>: [</span><span>main</span><span>]</span></div></div><div><div><div>6</div></div><div><span>  </span><span>pull_request</span><span>:</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>jobs</span><span>:</span></div></div><div><div><div>9</div></div><div><span>  </span><span>build</span><span>:</span></div></div><div><div><div>10</div></div><div><span>    </span><span>runs-on</span><span>: </span><span>ubuntu-latest</span></div></div><div><div><div>11</div></div><div><span>    </span><span>steps</span><span>:</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>- </span><span>uses</span><span>: </span><span>actions/checkout@v4</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>- </span><span>uses</span><span>: </span><span>actions/setup-node@v4</span></div></div><div><div><div>14</div></div><div><span>        </span><span>with</span><span>:</span></div></div><div><div><div>15</div></div><div><span>          </span><span>node-version</span><span>: </span><span>22</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>- </span><span>run</span><span>: </span><span>npm ci</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>- </span><span>run</span><span>: </span><span>npm run lint</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>- </span><span>run</span><span>: </span><span>npm test</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>- </span><span>run</span><span>: </span><span>npm run build</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>用缓存加速依赖安装<a href="#用缓存加速依赖安装"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>      </span></span><span>- </span><span>uses</span><span>: </span><span>actions/setup-node@v4</span></div></div><div><div><div>2</div></div><div><span>        </span><span>with</span><span>:</span></div></div><div><div><div>3</div></div><div><span>          </span><span>node-version</span><span>: </span><span>22</span></div></div><div><div><div>4</div></div><div><span>          </span><span>cache</span><span>: </span><span>npm</span><span>          </span><span># 自动缓存 ~/.npm</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对 pnpm / yarn 同样支持 <code>cache: pnpm</code>。</p></section>
<section><h2>用 Secrets 管理凭据<a href="#用-secrets-管理凭据"><span>#</span></a></h2><p>仓库 <code>Settings → Secrets</code> 中配置，workflow 里引用：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>      </span></span><span>- </span><span>name</span><span>: </span><span>Deploy</span></div></div><div><div><div>2</div></div><div><span>        </span><span>env</span><span>:</span></div></div><div><div><div>3</div></div><div><span>          </span><span>SSH_KEY</span><span>: </span><span>${{ secrets.SSH_KEY }}</span></div></div><div><div><div>4</div></div><div><span>        </span><span>run</span><span>: </span><span>./deploy.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>切勿</strong>把密钥写死在代码或日志里，GitHub 会自动对 secret 打码。</p></section>
<section><h2>多环境发布<a href="#多环境发布"><span>#</span></a></h2><p>用 <code>environment</code> 区分 staging / production，并可配置审批：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>deploy-prod</span><span>:</span></div></div><div><div><div>2</div></div><div><span>    </span><span>needs</span><span>: </span><span>build</span></div></div><div><div><div>3</div></div><div><span>    </span><span>environment</span><span>: </span><span>production</span></div></div><div><div><div>4</div></div><div><span>    </span><span>runs-on</span><span>: </span><span>ubuntu-latest</span></div></div><div><div><div>5</div></div><div><span>    </span><span>steps</span><span>:</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>- </span><span>run</span><span>: </span><span>./deploy.sh prod</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>小结<a href="#小结"><span>#</span></a></h2><p>GitHub Actions 的优势是<strong>零基础设施</strong>、YAML 即代码、与仓库深度集成。小团队用它做 lint+test+build 几乎零成本，规模上来后再引入矩阵构建与自托管 Runner。</p><blockquote><p>流水线的第一原则：<strong>能自动化的就不要手动</strong>。一次配置，长期受益。</p></blockquote></section>]]></content>
    </entry>
    <entry>
      <id>https://www.devng.cn/posts/typescript-generics/</id>
      <title type="text">TypeScript 泛型与类型体操入门</title>
      <published>2026-08-06T00:00:00.000Z</published>
      <updated>2026-08-06T00:00:00.000Z</updated>
      <author><name>CyreneStar</name></author>
      <link rel="alternate" href="https://www.devng.cn/posts/typescript-generics/"/>
      <summary type="text">从泛型基础到 Utility Types 与条件类型，循序渐进掌握 TypeScript 类型层面的抽象能力，写出更安全的代码。</summary>
      <content type="html"><![CDATA[<section><h2>为什么需要泛型<a href="#为什么需要泛型"><span>#</span></a></h2><p>泛型让函数/类型在<strong>不丢失类型信息</strong>的前提下复用。没有泛型时只能退回 <code>any</code>，连带丢掉所有类型检查。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>identity</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>value</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>T</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span><span> </span><span>value</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>a</span><span> </span><span>=</span><span> </span><span>identity</span><span><span>&lt;</span><span>string</span><span>&gt;(</span></span><span>'hello'</span><span>)  </span><span>// a: string</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>b</span><span> </span><span>=</span><span> </span><span>identity</span><span>(</span><span>42</span><span>)               </span><span>// b: number（自动推断）</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>泛型约束<a href="#泛型约束"><span>#</span></a></h2><p>用 <code>extends</code> 限制入参必须包含某些形状：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>pluck</span><span>&lt;</span><span>T</span><span>, </span><span>K</span><span> </span><span>extends</span><span> </span><span>keyof</span><span> </span><span>T</span><span><span>&gt;(</span><span>obj</span></span><span>:</span><span> </span><span>T</span><span><span>, </span><span>key</span></span><span>:</span><span> </span><span>K</span><span>)</span><span>:</span><span> </span><span>T</span><span>[</span><span>K</span><span>] {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span><span> </span><span>obj</span><span>[</span><span>key</span><span>]</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>pluck</span><span>({ </span><span>name</span><span>:</span><span> </span><span>'Cyrene'</span><span>, </span><span>age</span><span>:</span><span> </span><span>18</span><span> }, </span><span>'name'</span><span>)  </span><span>// string</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>常用 Utility Types<a href="#常用-utility-types"><span>#</span></a></h2>

<table><thead><tr><th>类型</th><th>作用</th></tr></thead><tbody><tr><td><code>Partial&lt;T&gt;</code></td><td>所有属性变可选</td></tr><tr><td><code>Required&lt;T&gt;</code></td><td>所有属性变必填</td></tr><tr><td><code>Pick&lt;T, K&gt;</code></td><td>只取部分属性</td></tr><tr><td><code>Omit&lt;T, K&gt;</code></td><td>排除部分属性</td></tr><tr><td><code>Record&lt;K, V&gt;</code></td><td>构造键值映射类型</td></tr></tbody></table><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>type</span><span> </span><span>User</span><span> </span><span>=</span><span><span> { </span><span>id</span></span><span>:</span><span> </span><span>number</span><span><span>; </span><span>name</span></span><span>:</span><span> </span><span>string</span><span><span>; </span><span>email</span></span><span>:</span><span> </span><span>string</span><span> }</span></div></div><div><div><div>2</div></div><div><span>type</span><span> </span><span>UserPreview</span><span> </span><span>=</span><span> </span><span>Pick</span><span>&lt;</span><span>User</span><span>, </span><span>'id'</span><span> </span><span>|</span><span> </span><span>'name'</span><span>&gt;   </span><span>// { id; name }</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>条件类型与 infer<a href="#条件类型与-infer"><span>#</span></a></h2><p>条件类型像类型的三元表达式，<code>infer</code> 可”提取”内部类型：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>type</span><span> </span><span>ElementType</span><span>&lt;</span><span>T</span><span>&gt; </span><span>=</span><span> </span><span>T</span><span> </span><span>extends</span><span> (</span><span>infer</span><span> </span><span>U</span><span>)[] </span><span>?</span><span> </span><span>U</span><span> </span><span>:</span><span> </span><span>T</span></div></div><div><div><div>2</div></div><div><span>type</span><span> </span><span>A</span><span> </span><span>=</span><span> </span><span>ElementType</span><span><span>&lt;</span><span>number</span><span>[]&gt;   </span></span><span>// number</span></div></div><div><div><div>3</div></div><div><span>type</span><span> </span><span>B</span><span> </span><span>=</span><span> </span><span>ElementType</span><span><span>&lt;</span><span>string</span><span>&gt;     </span></span><span>// string</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>ReturnType</code> 就是基于此实现：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>type</span><span> </span><span>R</span><span> </span><span>=</span><span> </span><span>ReturnType</span><span>&lt;() </span><span>=&gt;</span><span> </span><span>Date</span><span>&gt;   </span><span>// Date</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>实战：安全取值<a href="#实战安全取值"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>get</span><span>&lt;</span><span>T</span><span>, </span><span>K</span><span> </span><span>extends</span><span> </span><span>keyof</span><span> </span><span>T</span><span><span>&gt;(</span><span>obj</span></span><span>:</span><span> </span><span>T</span><span><span>, </span><span>key</span></span><span>:</span><span> </span><span>K</span><span>)</span><span>:</span><span> </span><span>T</span><span>[</span><span>K</span><span>] </span><span>|</span><span> </span><span>undefined</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span><span> </span><span>obj</span><span>[</span><span>key</span><span>]</span></span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p>类型体操不是为了炫技，而是把<strong>运行时会崩的错</strong>提前到编译期。克制使用，读懂胜过写炫。</p></blockquote></section>]]></content>
    </entry>
</feed>
