Published on

每个开发人员都应该知道的 TypeScript 技巧

Authors

这段时间运用typescript有了更深入的了解,在此之前,一直使用的javascript开发项目,最近在做的一个项目恰巧用到了,然后就开启了边学边做的历程,经过这段时间的摸索,也总结一些技巧,以下总结的几点:

1. satisfies用于更好的类型推断

该运算符验证表达式是否与类型匹配,同时保留字面量类型,这对于需要兼顾类型安全性和精确推断的配置对象尤为重要。

// 不使用
const Config1: Record<string, string | number> = {
  port: 3000,
  host: 'localhost',
}

// 使用
const Config2 = {
  port: 3000,
  host: 'localhost',
} satisfies Record<string, string | number>

2. 不可变类型的Const断言

添加as const到对象后,Typescript会将所有属性变为只读属性,并推断出具体的类型,这对于不可更改配置对象尤为重要

const User = {
    name: 'zhansan'
    age: 18,
    sex: 'man'
} as Const

3. 模板字符串字面量类型

模板字面量类型允许你创建与特定字符串匹配的类型,适合用于API端点,事件名称和任何结构化字符串

type EventName = `on${Capitalize<string>}`
// 正确示范:'onChange' 'onHandle' onClick'
// 错误示范:'change' 'handle' 'click'

type Method = 'GET' | 'POST' | 'DELETE' | 'PUT'
type Endpoint = `/api/${string}`
type Route = `${Method} ${Endpoint}`
// 正确示范:GET /api/users
// 错误示范:GET /users

function MakeRoute(route: Route): void {
  return route
}

MakeRoute('GET /api/users')

4. 自定义类型的关键字定义

在创建函数时指定对象类型

function isString(value: unKnown) value is string {
    return typeof value === 'string'
}

5. 使用索引访问类型

使用索引访问类型

使用括号表示法访问类型定义,这可以保持类型的DRY原则,并在源类型发生变化保持更新。

type User = { name: string; age: number }
type UserName = User['name'] // string

6. 三元运算符动态判断对象类型

使用条件判断对象类型,常见的使用三元运算符判断

    type isArray<T> = T extends any[] ? true : false

    // 提取数据元素类型
    type Flatten<T> = T extends Array[infer U] ? U : T
    type Flatten1 = Flatten<string[]> // string
    type Flatten2 = Flatten<number> // number

    // 其他案列
    type ApiResponse<T> = T extends {error: string} ? {success: false, error: string : {success: true, data: T}

7. 函数重载,可以更好的得到编辑器的提示

使用函数重载,获得更好的提示与准确的类型保护

function getUserById(id: string): User {}

// override
function getUserById(id: string): User {
    // 写逻辑判断
    const user = {}
    return user
}

8. keyof类型约束

可以使用keyof限制传递类型,可以更好避免类型推断错误

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

9. 模块扩充

根据需要扩展现有模块类型,通常是向窗口添加属性或者第三方库

declare global {
  interface Window {
    analytics: AnalyticsClient
  }
}

10. 类型导入

import type { User } from './types'

11. 断言函数

创建断言函数和缩小类型的函数,如果断言失败,函数将抛出异常,这对于在运行时验证数据并同时保证Typescript正常运行。

function assertDefined<T>(value: T | undefined): asserts value is T {
  if (value === undefined) {
    throw new Error('value is undefined')
  }
}

// 自定义类型谓词 (asserts value is T)是语法
// 使用
function processUser(user: User | undefined) {
  assertDefined(user)
  console.log(user.name)
}

12. 名义类型模式(品牌类型模式)

这种模式是将相同类型但是又需要有区别人为制造差异化,定义一个_brand虚拟属性,从而实现类型定义更准确。

type UserId = string & { _brand: 'UserId' }
type PostId = string & { _brand: 'PostId' }

function createUserId(id: string): UserId {
  return id as UserId
}

function createPostId(id: string): PostId {
  return id as PostId
}

const userId: UserId = createUserId('12345')
const postId: PostId = createPostId('12345')

function getUserById(id: UserId) {
  /** */
}

getUserById(userId) // 正确
getUserById(postId) // 报错

13. 使用枚举

const enum status = {
    Error = 'error'
    Success = 'success'
    Fail = 'fail'
}

14. 组合交叉类型

将多个类型合并为一个类型,对于合并不相关的类型比extends好用

type Name = string
type Age = number
type Worker = {
  address: string
  job: string
}

type User = Name & Age & Worker

总结

运用好类型判断,不仅能节省排除错误的时间,还能提高开发效率,虽然定义类型是一件痛苦的事情,但是后续项目维护起来那就是非常丝滑的操作了。后续如果有补充再继续完善!