作者:Daniel Bartholomae

翻译:疯狂的技术宅

原文链接:https://startup-cto.net/10-bad-typescript-habits-to-break-this-year/

近几年 TypeScript 和 JavaScript 一直在稳步发展。我们在过去写代码时养成了一些习惯,而有些习惯却没有什么意义。以下是我们都应该改正的 10 个坏习惯。

1.不使用 strict 模式

这种习惯看起来是什么样的

没有用严格模式编写 tsconfig.json。

{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs"
}
}

应该怎样

只需启用 strict 模式即可:

{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs",
"strict": true
}
}

为什么会有这种坏习惯

在现有代码库中引入更严格的规则需要花费时间。

为什么不该这样做

更严格的规则使将来维护代码时更加容易,使你节省大量的时间。

2. 用 || 定义默认值

这种习惯看起来是什么样的

使用旧的 || 处理后备的默认值:

function createBlogPost (text: string, author: string, date?: Date) {
return {
text: text,
author: author,
date: date || new Date()
}
}

应该怎样

使用新的 ?? 运算符,或者在参数重定义默认值。

function createBlogPost (text: string, author: string, date: Date = new Date())
return {
text: text,
author: author,
date: date
}
}

为什么会有这种坏习惯

?? 运算符是去年才引入的,当在长函数中使用值时,可能很难将其设置为参数默认值。

为什么不该这样做

?? 与 || 不同,?? 仅针对 null 或 undefined,并不适用于所有虚值。

3. 随意使用 any 类型

这种习惯看起来是什么样的

当你不确定结构时,可以用 any 类型。

async function loadProducts(): Promise<Product[]> {
const response = await fetch('https://api.mysite.com/products')
const products: any = await response.json()
return products
}

应该怎样

把你代码中任何一个使用 any 的地方都改为 unknown

async function loadProducts(): Promise<Product[]> {
const response = await fetch('https://api.mysite.com/products')
const products: unknown = await response.json()
return products as Product[]
}

为什么会有这种坏习惯

any 是很方便的,因为它基本上禁用了所有的类型检查。通常,甚至在官方提供的类型中都使用了 any。例如,TypeScript 团队将上面例子中的 response.json() 的类型设置为 Promise <any>。

为什么不该这样做

它基本上禁用所有类型检查。任何通过 any 进来的东西将完全放弃所有类型检查。这将会使错误很难被捕获到。

4. val as SomeType

这种习惯看起来是什么样的

强行告诉编译器无法推断的类型。

async function loadProducts(): Promise<Product[]> {
const response = await fetch('https://api.mysite.com/products')
const products: unknown = await response.json()
return products as Product[]
}

应该怎样

这正是 Type Guard 的用武之地。

function isArrayOfProducts (obj: unknown): obj is Product[] {
return Array.isArray(obj) && obj.every(isProduct)
} function isProduct (obj: unknown): obj is Product {
return obj != null
&& typeof (obj as Product).id === 'string'
} async function loadProducts(): Promise<Product[]> {
const response = await fetch('https://api.mysite.com/products')
const products: unknown = await response.json()
if (!isArrayOfProducts(products)) {
throw new TypeError('Received malformed products API response')
}
return products
}

为什么会有这种坏习惯

从 JavaScript 转到 TypeScript 时,现有的代码库通常会对 TypeScript 编译器无法自动推断出的类型进行假设。在这时,通过 as SomeOtherType 可以加快转换速度,而不必修改 tsconfig 中的设置。

为什么不该这样做

Type Guard 会确保所有检查都是明确的。

5. 测试中的 as any

这种习惯看起来是什么样的

编写测试时创建不完整的用例。

interface User {
id: string
firstName: string
lastName: string
email: string
} test('createEmailText returns text that greats the user by first name', () => {
const user: User = {
firstName: 'John'
} as any expect(createEmailText(user)).toContain(user.firstName)
}

应该怎样

如果你需要模拟测试数据,请将模拟逻辑移到要模拟的对象旁边,并使其可重用。

interface User {
id: string
firstName: string
lastName: string
email: string
} class MockUser implements User {
id = 'id'
firstName = 'John'
lastName = 'Doe'
email = 'john@doe.com'
} test('createEmailText returns text that greats the user by first name', () => {
const user = new MockUser() expect(createEmailText(user)).toContain(user.firstName)
}

为什么会有这种坏习惯

在给尚不具备广泛测试覆盖条件的代码编写测试时,通常会存在复杂的大数据结构,但要测试的特定功能仅需要其中的一部分。短期内不必关心其他属性。

为什么不该这样做

在某些情况下,被测代码依赖于我们之前认为不重要的属性,然后需要更新针对该功能的所有测试。

6. 可选属性

这种习惯看起来是什么样的

将属性标记为可选属性,即便这些属性有时不存在。

interface Product {
id: string
type: 'digital' | 'physical'
weightInKg?: number
sizeInMb?: number
}

应该怎样

明确哪些组合存在,哪些不存在。

interface Product {
id: string
type: 'digital' | 'physical'
} interface DigitalProduct extends Product {
type: 'digital'
sizeInMb: number
} interface PhysicalProduct extends Product {
type: 'physical'
weightInKg: number
}

为什么会有这种坏习惯

将属性标记为可选而不是拆分类型更容易,并且产生的代码更少。它还需要对正在构建的产品有更深入的了解,并且如果对产品的设计有所修改,可能会限制代码的使用。

为什么不该这样做

类型系统的最大好处是可以用编译时检查代替运行时检查。通过更显式的类型,能够对可能不被注意的错误进行编译时检查,例如确保每个 DigitalProduct 都有一个 sizeInMb。

7. 用一个字母通行天下

这种习惯看起来是什么样的

用一个字母命名泛型

function head<T> (arr: T[]): T | undefined {
return arr[0]
}

应该怎样

提供完整的描述性类型名称。

function head<Element> (arr: Element[]): Element | undefined {
return arr[0]
}

为什么会有这种坏习惯

这种写法最早来源于C++的范型库,即使是 TS 的官方文档也在用一个字母的名称。它也可以更快地输入,只需要简单的敲下一个字母 T 就可以代替写全名。

为什么不该这样做

通用类型变量也是变量,就像其他变量一样。当 IDE 开始向我们展示变量的类型细节时,我们已经慢慢放弃了用它们的名称描述来变量类型的想法。例如我们现在写代码用 const name ='Daniel',而不是 const strName ='Daniel'。同样,一个字母的变量名通常会令人费解,因为不看声明就很难理解它们的含义。

8. 对非布尔类型的值进行布尔检查

这种习惯看起来是什么样的

通过直接将值传给 if 语句来检查是否定义了值。

function createNewMessagesResponse (countOfNewMessages?: number) {
if (countOfNewMessages) {
return `You have ${countOfNewMessages} new messages`
}
return 'Error: Could not retrieve number of new messages'
}

应该怎样

明确检查我们所关心的状况。

function createNewMessagesResponse (countOfNewMessages?: number) {
if (countOfNewMessages !== undefined) {
return `You have ${countOfNewMessages} new messages`
}
return 'Error: Could not retrieve number of new messages'
}

为什么会有这种坏习惯

编写简短的检测代码看起来更加简洁,使我们能够避免思考实际想要检测的内容。

为什么不该这样做

也许我们应该考虑一下实际要检查的内容。例如上面的例子以不同的方式处理 countOfNewMessages 为 0 的情况。

9. ”棒棒“运算符

这种习惯看起来是什么样的

将非布尔值转换为布尔值。

function createNewMessagesResponse (countOfNewMessages?: number) {
if (!!countOfNewMessages) {
return `You have ${countOfNewMessages} new messages`
}
return 'Error: Could not retrieve number of new messages'
}

应该怎样

明确检查我们所关心的状况。

function createNewMessagesResponse (countOfNewMessages?: number) {
if (countOfNewMessages !== undefined) {
return `You have ${countOfNewMessages} new messages`
}
return 'Error: Could not retrieve number of new messages'
}

为什么会有这种坏习惯

对某些人而言,理解 !! 就像是进入 JavaScript 世界的入门仪式。它看起来简短而简洁,如果你对它已经非常习惯了,就会知道它的含义。这是将任意值转换为布尔值的便捷方式。尤其是在如果虚值之间没有明确的语义界限时,例如 null、undefined 和 ''。

为什么不该这样做

与很多编码时的便捷方式一样,使用 !! 实际上是混淆了代码的真实含义。这使得新开发人员很难理解代码,无论是对一般开发人员来说还是对 JavaScript 来说都是新手。也很容易引入细微的错误。在对“非布尔类型的值”进行布尔检查时 countOfNewMessages 为 0 的问题在使用 !! 时仍然会存在。

10. != null

这种习惯看起来是什么样的

棒棒运算符的小弟 ! = null使我们能同时检查 null 和 undefined。

function createNewMessagesResponse (countOfNewMessages?: number) {
if (countOfNewMessages != null) {
return `You have ${countOfNewMessages} new messages`
}
return 'Error: Could not retrieve number of new messages'
}

应该怎样

明确检查我们所关心的状况。

function createNewMessagesResponse (countOfNewMessages?: number) {
if (countOfNewMessages !== undefined) {
return `You have ${countOfNewMessages} new messages`
}
return 'Error: Could not retrieve number of new messages'
}

为什么会有这种坏习惯

如果你的代码在 null 和 undefined 之间没有明显的区别,那么 != null 有助于简化对这两种可能性的检查。

为什么不该这样做

尽管 null 在 JavaScript早期很麻烦,但 TypeScript 处于 strict 模式时,它却可以成为这种语言中宝贵的工具。一种常见模式是将 null 值定义为不存在的事物,将 undefined 定义为未知的事物,例如 user.firstName === null 可能意味着用户实际上没有名字,而 user.firstName === undefined 只是意味着我们尚未询问该用户(而 user.firstName === 的意思是字面意思是 '' 。

点击关注,第一时间了解华为云新鲜技术~

因为这几个TypeScript代码的坏习惯,同事被罚了500块的更多相关文章

  1. 【重构】 代码的坏味道总结 Bad Smell (一) (重复代码 | 过长函数 | 过大的类 | 过长参数列 | 发散式变化 | 霰弹式修改)

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

  2. Bad Smell (代码的坏味道)

    sourcemaking 如果一段代码是不稳定或者有一些潜在问题的,那么代码往往会包含一些明显的痕迹.正如食物要腐坏之前,经常会发出一些异味一样, 我们管这些痕迹叫做 "代码异味" ...

  3. 重构 之 总结代码的坏味道 Bad Smell (一) 重复代码 过长函数 过大的类 过长参数列 发散式变化 霰弹式修改

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

  4. Refactoring之——代码的坏味道(一)过长方法

    1 代码的坏味道 重构一书中提到了22种代码的坏味道,大致可以分为几类. 识别代码的坏味道,有助于发现代码的潜在问题,从而可以有的放矢的修改现有代码,使之不断完善. 1.1 Bloaters(臭鲱,暂 ...

  5. Chapter 3 :代码的坏味道

    "如果尿布臭了,就换掉它." --Beck奶奶,论保持小孩清洁的哲学 代码的坏味道这一章集中论述该何时重构.具体的重构方法在后面的章节. "没有任何度量规矩比得上见识广博 ...

  6. TypeScript TSLint(TypeScript代码检查工具)

    TSLint是TypeScript代码的样式风格检查工具.类似于JavaScript的ESLint,或者Ruby的Rubocop. 配置TSLint TSLint是一个外部工具,我们需要进行一次安装工 ...

  7. 消灭 Java 代码的“坏味道”

    消灭 Java 代码的“坏味道” 原创: 王超 阿里巴巴中间件 昨天 导读 明代王阳明先生在<传习录>谈为学之道时说: 私欲日生,如地上尘,一日不扫,便又有一层.着实用功,便见道无终穷,愈 ...

  8. Sonar中的坏习惯详解

    22种代码的坏味道,一句话概括: 如果一段代码是不稳定或者有一些潜在问题的,那么代码往往会包含一些明显的痕迹. 正如食物要腐坏之前,经常会发出一些异味一样. 我们管这些痕迹叫做“代码异味”. 参考资料 ...

  9. 使用Bugfree不应有的坏习惯

    Bugfree是一款优秀的bug管理和追踪工具,因此受到不少公司的青睐.但实际的工作中,我发现不少开发或是测试的同事有一些不好的使用习惯,使得我们对Bugfree的利用不够高效.我下面列出使用Bugf ...

随机推荐

  1. NGK全球启动大会圆满召开,一起预见区块链的美好未来!

    NGK Global全球启动大会在高新技术产业区硅谷于美国当地时间11月25日圆满召开!这次会议邀请了星盟高管.NGK全球各大市场领导人.NGK生态产业代表等上百位嘉宾出席,此次会议持续了一个多小时, ...

  2. 实现TensorRT-7.0插件自由!(如果不踩坑使用TensorRT插件功能)

    本系列为新TensorRT的第一篇,为什么叫新,因为之前已经写了两篇关于TensorRT的文章,是关于TensorRT-5.0版本的.好久没写关于TensorRT的文章了,所幸就以新来开头吧~ 接下来 ...

  3. MySQL命名、设计及使用规范

    本文转载自MySQL命名.设计及使用规范 导语 最近在看MySQL相关的内容,整理如下规范,作为一名刚刚学习MySQL的菜鸟,整理的内容非常的基础,中间可能涉及到有错误的地方,欢迎批评指正,看到有错误 ...

  4. Hystrix熔断器的使用步骤

    1.添加熔断器依赖 2.在配置文件中开启熔断器 feign.hystrix.enabled=true 3.写接口的实现类VodFileDegradeFeignClient,在实现类中写如果出错了输出的 ...

  5. 又长又细,万字长文带你解读Redisson分布式锁的源码

    前言 上一篇文章写了Redis分布式锁的原理和缺陷,觉得有些不过瘾,只是简单的介绍了下Redisson这个框架,具体的原理什么的还没说过呢.趁年前项目忙的差不多了,反正闲着也是闲着,不如把Rediss ...

  6. Docker 镜像加速教程

    原文链接:https://fuckcloudnative.io/posts/docker-registry-proxy/ 在使用 Docker 和 Kubernetes 时,我们经常需要访问 gcr. ...

  7. gojs插件使用教程

    目录 一.简介 二.简单使用 三.重要概念 1.TextBlock创建文本 2.Shape图形 3.Node节点(文本与图形结合) 4.Link箭头 四.数据绑定(前后端交互数据渲染) 五.去除水印 ...

  8. 微信小程序使用彩色图标(阿里巴巴 iconfont Symbol 的用法)微信小程序使用彩色图标(阿里巴巴 iconfont Symbol 的用法)

    前提 需要安装好 nodejs (略) 用于下载插件 安装插件 npm install mini-program-iconfont-cli --save-dev 初始化配置文件 npx iconfon ...

  9. Linux磁盘分区格式化和扩容

    Note:根据各系统上磁盘的类型不同,磁盘命名规则也会不同:例如/dev/xvd,/dev/sd,/dev/vd,/dev/hd 目录 磁盘格式化 MBR格式 GPT分区 磁盘扩容 MBR格式扩容 G ...

  10. PAT-1154(Vertex Coloring )+map使用+vector建图+set的使用

    Vertex Coloring PAT-1154 #include<iostream> #include<cstring> #include<string> #in ...