关于typescript:因为这几个TypeScript代码的坏习惯同事被罚了500块

34次阅读

共计 5919 个字符,预计需要花费 15 分钟才能阅读完成。

作者: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
  }
}

为什么会有这种坏习惯

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

为什么不该这样做

?? 与 || 不同,?? 仅针对 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 === 的意思是字面意思是 ”。

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0