乐趣区

关于typescript:写TypeScript代码的10种坏习惯

作者:Daniel Bartholomae

翻译:疯狂的技术宅

原文:https://startup-cto.net/10-ba…

近几年 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
  }
}

为什么会有这种坏习惯

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

为什么不该这样做

??|| 不同,?? 仅针对 nullundefined,并不适用于所有虚值。

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'
}

为什么会有这种坏习惯

编写简短的检测代码看起来更加简洁,使咱们可能防止思考理论想要检测的内容。

为什么不该这样做

兴许咱们应该考虑一下理论要查看的内容。例如下面的例子以不同的形式解决 countOfNewMessages0 的状况。

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 世界的入门典礼。它看起来简短而简洁,如果你对它曾经十分习惯了,就会晓得它的含意。这是将任意值转换为布尔值的便捷形式。尤其是在如果虚值之间没有明确的语义界线时,例如 nullundefined''

为什么不该这样做

与很多编码时的便捷形式一样,应用 !! 实际上是混同了代码的实在含意。这使得新开发人员很难了解代码,无论是对个别开发人员来说还是对 JavaScript 来说都是老手。也很容易引入轻微的谬误。在对“非布尔类型的值”进行布尔查看时 countOfNewMessages0 的问题在应用 !! 时依然会存在。

10. != null

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

棒棒运算符的小弟 ! = null 使咱们能同时查看 nullundefined

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'
}

为什么会有这种坏习惯

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

为什么不该这样做

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


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章


欢送持续浏览本专栏其它高赞文章:

  • 深刻了解 Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13 个帮你进步开发效率的古代 CSS 框架
  • 疾速上手 BootstrapVue
  • JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
  • WebSocket 实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30 分钟用 Node.js 构建一个 API 服务器
  • Javascript 的对象拷贝
  • 程序员 30 岁前月薪达不到 30K,该何去何从
  • 14 个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把 HTML 转成 PDF 的 4 个计划及实现

  • 更多文章 …
退出移动版