Skip to content

Commit

Permalink
260
Browse files Browse the repository at this point in the history
  • Loading branch information
ascoders committed Oct 24, 2022
1 parent 0b76d01 commit f135d9e
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

前端界的好文精读,每周更新!

最新精读:<a href="./前沿技术/259.%E7%B2%BE%E8%AF%BB%E3%80%8AHeadless%20%E7%BB%84%E4%BB%B6%E7%94%A8%E6%B3%95%E4%B8%8E%E5%8E%9F%E7%90%86%E3%80%8B.md">259.精读《Headless 组件用法与原理》</a>
最新精读:<a href="./前沿技术/260.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A6%82%E4%BD%95%E4%B8%BA%20TS%20%E7%B1%BB%E5%9E%8B%E5%86%99%E5%8D%95%E6%B5%8B%E3%80%8B.md">260.精读《如何为 TS 类型写单测》</a>

素材来源:[周刊参考池](https://github.com/ascoders/weekly/issues/2)

Expand Down Expand Up @@ -198,6 +198,7 @@
- <a href="./前沿技术/257.%E7%B2%BE%E8%AF%BB%E3%80%8AState%20of%20CSS%202022%E3%80%8B.md">257.精读《State of CSS 2022》</a>
- <a href="./前沿技术/258.%E7%B2%BE%E8%AF%BB%E3%80%8Aproposal-extractors%E3%80%8B.md">258.精读《proposal-extractors》</a>
- <a href="./前沿技术/259.%E7%B2%BE%E8%AF%BB%E3%80%8AHeadless%20%E7%BB%84%E4%BB%B6%E7%94%A8%E6%B3%95%E4%B8%8E%E5%8E%9F%E7%90%86%E3%80%8B.md">259.精读《Headless 组件用法与原理》</a>
- <a href="./前沿技术/260.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A6%82%E4%BD%95%E4%B8%BA%20TS%20%E7%B1%BB%E5%9E%8B%E5%86%99%E5%8D%95%E6%B5%8B%E3%80%8B.md">260.精读《如何为 TS 类型写单测》</a>

### TS 类型体操

Expand Down
60 changes: 60 additions & 0 deletions 前沿技术/260.精读《如何为 TS 类型写单测》.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
如何为 TS 类型写单测呢?

最简单的办法就是试探性访问属性,如果该属性访问不到自然会在异常时出现错误,如:

```ts
import { myLib } from "code";
myLib.update; // 正确
```

如上所示,如果 `myLib` 没有正确的开放 `update` 属性将会提示错误。但这种单测并不是我们要讲的类型。想一想,如果我们只开放 `.update` API 给用户,但框架内部可以使用全量的 `.update``.add``.remove` 方法,如何验证框架没有把不必要的属性也开放给了用户呢?

一种做法是直接访问类型提示,此时会出现错误下划线:

```ts
myLib.add
~~~ // Property 'add' does not exist on type MyLib
```

此时说明代码逻辑正常,但却抛出了 ts 错误,这可能会阻塞 CI 流程,而且我们也无从判断这个报错是否 “实际山是逻辑正确的表现”,所以 “不能出现某个属性” 就不能以直接访问属性的方式实现了,我们要做一些曲线方案。

## 利用特殊类型方法

我们可以利用 `extends` 构造三元类型表达式,逻辑是如果 `myLib` 拥有 `.add` 属性就返回 a 类型,否则返回 b 类型。因为 `myLib` 不该提供 `.add` 属性,所以下一步判断该新类型一定符合 b 即可:

```ts
const check: typeof myLib extends { add: any } ? number : number[] = [];
check.length; // 该行在没有 .add 属性时不会报错,反之则报错
```

因为我们给的默认值是字符串,而预期正确的结果也是进入 `number[]` 类型分支,所以 `check.length` 正常,如果某次改动误将 `.add` 提供了出来,`check.length` 就会报错,因为我们给值 `[]` 定义了 `number` 类型,访问 `.length` 属性肯定会出错。

## 利用赋值语句判断

另一种简化的办法是利用 `true` or `false` 判断变量类型是否匹配,如:

```ts
const check: typeof fn extends (a: any) => any ? true : false = true;
```

如果 `fn` 满足 `(a: any) => any` 类型,则 `check` 的类型限定为 `true`,否则为 `false`,所以当 `fn` 满足条件时该表达式正确,当 `fn` 不满足条件式,我们将变量 `true` 赋值给类型 `false` 的对象,会出现报错。

## 可以将 ts 转换为 js 吗?

也许你会有疑问,可以将 ts 类型校验错误转换为 js 对象吗?这样就可以用 `expect` 等断言结合到测试框架流程中了。很可惜,至少现在是不行的,只能做到利用 js 变量推导类型,不能利用类型生成变量。

## 总结

总结一下,如果想判断某些类型定义未暴露给用户,而实际上在 js 变量里是拥有这些属性的,就只能用类型方案判断正确性了。

比如变量 `myLib` 实际上拥有 `.update``.add` 方法,但提供给用户的类型定义刻意将 `.add` 隐藏了,此时校验方式是,利用一个跳板变量 `check`,使用 `extends` 判断其是否包含 `add` 属性,再利用特殊类型方法或者直接用赋值语句判断 `extends` 是否成立。

> 讨论地址是:[精读《如何为 TS 类型写单测》· Issue #446 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/446)
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**

> 关注 **前端精读微信公众号**
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)

0 comments on commit f135d9e

Please sign in to comment.