typescript常见高级技巧总结

作者:程序员山月 时间:2024-05-08 10:10:10 

用了一段时间的 typescript 之后,深感中大型项目中 typescript 的必要性,它能够提前在编译期避免许多 bug,如很恶心的拼写问题。而越来越多的 package 也开始使用 ts,学习 ts 已是势在必行。

以下是我在工作中总结到的比较实用的 typescript 技巧。

01 keyof

keyofObject.keys 略有相似,只不过 keyofinterface 的键。

interface Point {
   x: number;
   y: number;
}

// type keys = "x" | "y"
type keys = keyof Point;

假设有一个 object 如下所示,我们需要使用 typescript 实现一个 get 函数来获取它的属性值

const data = {
 a: 3,
 hello: 'world'
}

function get(o: object, name: string) {
 return o[name]
}

我们刚开始可能会这么写,不过它有很多缺点

  • 无法确认返回类型:这将损失 ts 最大的类型校验功能

  • 无法对 key 做约束:可能会犯拼写错误的问题

这时可以使用 keyof 来加强 get 函数的类型功能,有兴趣的同学可以看看 _.get 的 type 标记以及实现

function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
 return o[name]
}

02 Required & Partial & Pick

既然了解了 keyof,可以使用它对属性做一些扩展, 如实现 PartialPickPick 一般用在 _.pick

type Partial<T> = {
 [P in keyof T]?: T[P];
};

type Required<T> = {
 [P in keyof T]-?: T[P];
};

type Pick<T, K extends keyof T> = {
 [P in K]: T[P];
};

interface User {
 id: number;
 age: number;
 name: string;
};

// 相当于: type PartialUser = { id?: number; age?: number; name?: string; }
type PartialUser = Partial<User>

// 相当于: type PickUser = { id: number; age: number; }
type PickUser = Pick<User, "id" | "age">

这几个类型已内置在 Typescript 中

03 Condition Type

类似于 js 中的 ?: 运算符,可以使用它扩展一些基本类型

T extends U ? X : Y

type isTrue<T> = T extends true ? true : false
// 相当于 type t = false
type t = isTrue<number>

// 相当于 type t = false
type t1 = isTrue<false>

04 never & Exclude & Omit

官方文档对 never 的描述如下

the never type represents the type of values that never occur.

结合 neverconditional type 可以推出很多有意思而且实用的类型,比如 Omit

type Exclude<T, U> = T extends U ? never : T;

// 相当于: type A = 'a'
type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>

结合 Exclude 可以推出 Omit 的写法

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

interface User {
 id: number;
 age: number;
 name: string;
};

// 相当于: type PickUser = { age: number; name: string; }
type OmitUser = Omit<User, "id">

05 typeof

顾名思义,typeof 代表取某个值的 type,可以从以下示例来展示他们的用法

const a: number = 3

// 相当于: const b: number = 4
const b: typeof a = 4

在一个典型的服务端项目中,我们经常需要把一些工具塞到 context 中,如config,logger,db models, utils 等,此时就使用到 typeof

import logger from './logger'
import utils from './utils'

interface Context extends KoaContect {
 logger: typeof logger,
 utils: typeof utils
}

app.use((ctx: Context) => {
 ctx.logger.info('hello, world')

// 会报错,因为 logger.ts 中没有暴露此方法,可以最大限度的避免拼写错误
 ctx.loger.info('hello, world')
})

06 is

在此之前,先看一个 koa 的错误处理流程,以下是对 error 进行集中处理,并且标识 code 的过程

app.use(async (ctx, next) => {
 try {
   await next();
 } catch (err) {
   let code = 'BAD_REQUEST'
   if (err.isAxiosError) {
     code = `Axios-${err.code}`
   } else if (err instanceof Sequelize.BaseError) {

}
   ctx.body = {
     code
   }
 }
})

err.code 处,会编译出错,提示 Property 'code' does not exist on type 'Error'.ts(2339)

此时可以使用 as AxiosError 或者 as any 来避免报错,不过强制类型转换也不够友好

if ((err as AxiosError).isAxiosError) {
 code = `Axios-${(err as AxiosError).code}`
}

此时可以使用 is 来判定值的类型

function isAxiosError (error: any): error is AxiosError {
 return error.isAxiosError
}

if (isAxiosError(err)) {
 code = `Axios-${err.code}`
}

GraphQL 的源码中,有很多诸如此类的用法,用以标识类型

export function isType(type: any): type is GraphQLType;

export function isScalarType(type: any): type is GraphQLScalarType;

export function isObjectType(type: any): type is GraphQLObjectType;

export function isInterfaceType(type: any): type is GraphQLInterfaceType;

07 interface & type

interfacetype 的区别是什么?可以参考以下 stackoverflow 的问题

stackoverflow.com/questions/3&hellip;

一般来说,interfacetype 区别很小,比如以下两种写法差不多

interface A {
 a: number;
 b: number;
};

type B = {
 a: number;
 b: number;
}

其中 interface 可以如下合并多个,而 type 只能使用 & 类进行连接。

interface A {
   a: number;
}

interface A {
   b: number;
}

const a: A = {
   a: 3,
   b: 4
}

08 Record & Dictionary & Many

这几个语法糖是从 lodash 的 types 源码中学到的,平时工作中的使用频率还挺高。

type Record<K extends keyof any, T> = {
   [P in K]: T;
};

interface Dictionary<T> {
 [index: string]: T;
};

interface NumericDictionary<T> {
 [index: number]: T;
};

const data:Dictionary<number> = {
 a: 3,
 b: 4
}

09 使用 const enum 维护常量表

相比使用字面量对象维护常量,const enum 可以提供更安全的类型检查

// 使用 object 维护常量
const TODO_STATUS {
 TODO: 'TODO',
 DONE: 'DONE',
 DOING: 'DOING'
}
// 使用 const enum 维护常量
const enum TODO_STATUS {
 TODO = 'TODO',
 DONE = 'DONE',
 DOING = 'DOING'
}

function todos (status: TODO_STATUS): Todo[];

todos(TODO_STATUS.TODO)

10 VS Code Tips & Typescript Command

使用 VS Code 有时会出现,使用 tsc 编译时产生的问题与 vs code 提示的问题不一致

找到项目右下角的 Typescript 字样,右侧显示它的版本号,可以点击选择 Use Workspace Version,它表示与项目依赖的 typescript 版本一直。

或者编辑 .vs-code/settings.json

{
 "typescript.tsdk": "node_modules/typescript/lib"
}

11 Typescript Roadmap

最后一条也是最重要的一条,翻阅 Roadmap,了解 ts 的一些新的特性与 bug 修复情况。

来源:https://juejin.cn/post/6844903863791648782

标签:typescript,技巧
0
投稿

猜你喜欢

  • 注册表单之死

    2008-08-07 13:02:00
  • Python将视频或者动态图gif逐帧保存为图片的方法

    2022-08-08 00:50:17
  • 五分钟带你搞懂python 迭代器与生成器

    2021-08-24 19:25:10
  • Web设计师的出路问题

    2009-06-08 13:07:00
  • 在Windows服务器下用Apache和mod_wsgi配置Python应用的教程

    2021-10-28 22:36:17
  • python Django框架实现自定义表单提交

    2021-01-04 14:52:42
  • python实现对求解最长回文子串的动态规划算法

    2023-11-09 10:18:49
  • 高效测试用例组织算法pairwise之Python实现方法

    2021-07-22 01:09:49
  • asp如何做一个看他爱不爱你的小测验?

    2010-07-11 21:16:00
  • 如何把小写数字转换成英文?

    2009-11-06 13:59:00
  • Python实现链表反转的方法分析【迭代法与递归法】

    2021-07-07 17:56:37
  • ASp.net下fckeditor配置图片上传最简单的方法

    2023-03-25 02:34:00
  • FSO如何一行行地读取文件?

    2010-06-10 18:40:00
  • Vue常用指令v-if与v-show的区别浅析

    2024-05-28 15:46:57
  • python SocketServer源码深入解读

    2023-04-15 18:38:44
  • Mysql 5.6添加修改用户名和密码的方法

    2024-01-20 09:02:57
  • PDO::inTransaction讲解

    2023-06-06 08:32:27
  • Pandas实现DataFrame的简单运算、统计与排序

    2022-03-29 02:09:54
  • 基于SQL Server OS的任务调度机制详解

    2024-01-14 22:17:59
  • python中time.ctime()实例用法

    2022-11-09 05:25:32
  • asp之家 网络编程 m.aspxhome.com