打破 10 个 Typescript 编程坏习惯
【CSDN 编者按】好习惯受益终生,坏习惯尽快改掉!
不使用严格模式
例子:不使用严格模式的 tsconfig.json
{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs"
}
}
应该怎样写?启用严格模式
{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs",
"strict": true
}
}
为什么有些人会那么写?在现有代码库中引入更严格的规则需要很大的时间成本。
为什么不应该这么做?更严格的规则将使得未来修改代码时更加容易,因此可以减少我们修复代码所花费的时间。
使用 || 定义默认值
例子:通过 || 填充可选值
function createBlogPost (text: string, author: string, date?: Date) {
return {
text: text,
author: author,
date: date || new Date()
}
}
应该怎样写?推荐使用 ?? 运算符,或者定义参数级 fallback。
function createBlogPost (text: string, author: string, date: Date = new Date())
return {
text: text,
author: author,
date: date
}
}
为什么有些人会那么写?
?? 操作符在去年才被引入,所以引入之前很多人会使用 || 来设置默认值。当在长函数中间使用值时,可能很难将它们设置为参数默认值。
为什么不应该这么做?
使用 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 (例如,上面示例中的 response.json() 被 TypeScript 团队定义为 Promise<any>)。
为什么不应该那么写?这样写基本上禁用所有类型检查。任何 any 类型的类型检查都将被跳过。这样,当类型结构的推断与运行期不一致时,代码错误才会保留出来,很容易引发一些不易发现的 bug。
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[]
}
应该怎么写?
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
}
为什么有些人会那么写?
为什么不应该那么写?
在测试中使用 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)
}
应该怎么写?如果你需要为测试而 mock 数据,那就将 mock 逻辑和 mock 的对象放在一起,并使其可重用。
interface User {
id: string
firstName: string
lastName: string
email: string
}
class MockUser implements User {
id = 'id'
firstName = 'John'
lastName = 'Doe'
email = '[email protected]'
}
test('createEmailText returns text that greats the user by first name', () => {
const user = new MockUser()
expect(createEmailText(user)).toContain(user.firstName)
}
为什么有些人会那么写?
为什么不应该那么写?
可选属性
例子:将有时存在有时不存在的属性标记为可选:
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
}
为什么有些人会那么写?
为什么不应该那么写?
只用一个字母描述泛型
function head<T> (arr: T[]): T | undefined {
return arr[0]
}
应该怎么写?给出完整的描述性类型名称
function head<Element> (arr: Element[]): Element | undefined {
return arr[0]
}
为什么有些人会那么写?
为什么不应该那么写?
非布尔类型用作布尔检查
例子:通过将值直接传递给 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'
}
为什么有些人会那么写?
为什么不应该那么写?
!! 操作符
例子:将非布尔值转换为布尔值。
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'
}
为什么有些人会那么写?
为什么不应该那么写?
!=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'
}