文章列表:

为了更好的阅读体验,  可以看.

一年前刚接触 Typescript 的时候, 觉得它加大了代码工作量. 写一大堆东西.为了找某个类型东奔西跑, 引入第三库还经常报错.

然而现在的我想说: 真香.

我们经常吐槽别人代码可维护性特别低, 总是希望别人能够主动的写注释, 可是写注释却没有任何方式可以进行约束. 这下好了, 类型就是最好的注释, 用 Typescript, 可以大大提高代码的可维护性.

一. 如何处理第三方库类型相关问题

Typescipt 所提供的第三方库类型定义不仅约束我们的输入调用, 还能为我们提供文档. 现在, NPM 上的第三方类型定义种类繁多,很难保证类型定义是正确的. 也很难保证所有使用的第三方库都有类型定义.

那么, 在这个充满未知的过程中,如何才能正确使用TypeScript中的第三方库呢?

下面列举了四种常见的无法正常工作的场景以及对应的解决方法:

  • 库本身没有自带类型定义

  • 库本身没有类型定义, 也没有相关的@type

  • 类型声明库有误

  • 类型声明报错

1. 库本身没有自带类型定义

查找不到相关的库类型. 举个栗子

在初次将 react 改造支持 typescript 时, 想必很多人都会遇到 module.hot 报错. 此时只需要安装对应的类型库即可.

安装 @types/webpack-env

2. 库本身没有类型定义, 也没有相关的@type

那只能自己声明一个了. 随便举个栗子.

declare module "lodash"

3. 类型声明库有误

  • 推动解决官方类型定义的问题, 提issue, pr

  • Import 后通过 extends 或者 merge 能力对原类型进行扩展

  • 忍受类型的丢失或不可靠性

  • 使用 // @ts-ignore  忽略

4. 类型声明报错

  • 在 compilerOptions 的添加"skipLibCheck": true, 曲线救国

二. 巧用类型收缩解决报错下面列举了几种常见的解决方法:

  • 类型断言

  • 类型守卫 typeof in instanceof 字面量类型保护

  • 双重断言

1、 类型断言

类型断言可以明确的告诉 TypeScript 值的详细类型,

在某些场景, 我们非常确认它的类型, 即使与 typescript 推断出来的类型不一致. 那我们可以使用类型断言.

语法如下:

<类型>值

值 as 类型 // 推荐使用这种语法. 因为<>容易跟泛型, react 中的语法起冲突

举个例子, 如下代码,  padding 值可以是 string , 也可以是 number, 虽然在代码里面写了 Array(), 我们明确的知道, padding 会被parseint 转换成 number 类型, 但类型定义依然会报错.

function padLeft(value: string, padding: string | number) {

// 报错: Operator '+' cannot be applied to

// types 'string | number' and 'number'

return Array(padding + 1).join(" ") + value;

}

解决方法, 使用类型断言. 告诉 typescript 这里我确认它是 number 类型, 忽略报错.

function padLeft(value: string, padding: string | number) {

// 正常

return Array(padding as number + 1).join(" ") + value;

}

但是如果有下面这种情况, 我们要写很多个 as 么?

function padLeft(value: string, padding: string | number) {

console.log((padding as number) + 3);

console.log((padding as number) + 2);

console.log((padding as number) + 5);

return Array((padding as number) + 1).join(' ') + value;

}

2、 类型守卫

类型守卫有以下几种方式, 简单的概括以下

  • typeof:  用于判断 "number","string","boolean"或 "symbol" 四种类型.

  • instanceof : 用于判断一个实例是否属于某个类

  • in: 用于判断一个属性/方法是否属于某个对象

  • 字面量类型保护

上面的例子中, 是 string | number 类型, 因此使用 typeof 来进行类型守卫. 例子如下:

function padLeft(value: string,padding: string | number) {

if (typeof padding === 'number') {

console.log(padding + 3); //正常

console.log(padding + 2); //正常

console.log(padding + 5); //正常

//正常

return Array(padding + 1).join(' ') value;

}

if (typeof padding === 'string') {

return padding + value;

}

}

相比较 类型断言 as , 省去了大量代码. 除了 typeof , 我们还有几种方式, 下面一一举例子.

  • instanceof :用于判断一个实例是否属于某个类

class Man {

handsome = 'handsome';

}

class Woman {

beautiful = 'beautiful';

}

function Human(arg: Man | Woman) {

if (arg instanceof Man) {

console.log(arg.handsome);

console.log(arg.beautiful); // error

} else {

// 这一块中一定是 Woman

console.log(arg.beautiful);

}

}

  • in : 用于判断一个属性/方法是否属于某个对象

interface B {

b: string;

}

interface A {

a: string;

}

function foo(x: A | B) {

if ('a' in x) {

return x.a;

}

return x.b;

}

  • 字面量类型保护

有些场景, 使用 in, instanceof, typeof 太过麻烦. 这时候可以自己构造一个字面量类型.

type Man = {

handsome: 'handsome';

type: 'man';

};

type Woman = {

beautiful: 'beautiful';

type: 'woman';

};

function Human(arg: Man | Woman) {

if (arg.type === 'man') {

console.log(arg.handsome);

console.log(arg.beautiful); // error

} else {

// 这一块中一定是 Woman

console.log(arg.beautiful);

}

}

3、双重断言

有些时候使用 as 也会报错,因为 as 断言的时候也不是毫无条件的. 它只有当S类型是T类型的子集,或者T类型是S类型的子集时,S能被成功断言成T.

所以面对这种情况, 只想暴力解决问题的情况, 可以使用双重断言.

function handler(event: Event) {

const element = event as HTMLElement;

// Error: 'Event' 和 'HTMLElement'

中的任何一个都不能赋值给另外一个

}

如果你仍然想使用那个类型,你可以使用双重断言。首先断言成兼容所有类型的any

function handler(event: Event) {

const element = (event as any) as HTMLElement;

// 正常

}

三. 巧用 typescript 支持的 js 最新特性优化代码

1. 可选链 Optional Chaining

let x = foo?.bar.baz();

typescript 中的实现如下:

var _a;

let x = (_a = foo) === null ||

_a === void 0 ? void 0 : _a.bar.baz();

利用这个特性, 我们可以省去写很多恶心的 a && a.b && a.b.c 这样的代码

2. 空值联合 Nullish Coalescing

let x = foo ?? '22';

typescript 中的实现如下:

let x = (foo !== null && foo !== void 0 ?

foo : '22');

四. 巧用高级类型灵活处理数据typescript 提供了一些很不错的工具函数. 如下图

  • 类型索引

为了实现上面的工具函数, 我们需要先了解以下几个语法:

keyof : 获取类型上的 key 值

extends : 泛型里面的约束

T[K] : 获取对象 T 相应 K 的元素类型

type Partial<T> = {

[P in keyof T]?: T[P]

}

在使用 props 的时候, 有时候全部属性都是可选的, 如果一个一个属性写 ? , 大量的重复动作. 这种时候可以直接使用 Partial<State>

Record 作为一个特别灵活的工具. 第一个泛型传入对象的key值, 第二个传入 对象的属性值.

type Record<K extends string, T> = {

[P in K]: T;

}

我们看一下下面的这个对象, 你会怎么用 ts 声明它?

const AnimalMap = {

cat: { name: '猫', title: 'cat' },

dog: { name: '狗', title: 'dog' },

frog: { name: '蛙', title: 'wa' },

};

此时用 Record 即可.

type AnimalType = 'cat' | 'dog' | 'frog';

interface AnimalDescription {

name: string, title: string

}

const AnimalMap:

Record<AnimalType, AnimalDescription> = {

cat: { name: '猫', title: 'cat' },

dog: { name: '狗', title: 'dog' },

frog: { name: '蛙', title: 'wa' },

};

  • never, 构造条件类型

除了上面的几个语法. 我们还可以用 never , 构造条件类型来组合出更灵活的类型定义.

语法:

never: 从未出现的值的类型

// 如果 T 是 U 的子类型的话,那么就会返回 X,否则返回 Y

构造条件类型 : T extends U ? X : Y

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

// 相当于: type A = 'a'

type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>

  • 更简洁的修饰符: - 与 +

可以直接去除 ? 将所有对象属性变成必传内容.

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

// Remove readonly

type MutableRequired<T> = {

-readonly [P in keyof T]: T[P]

};

  • infer: 在 extends 条件语句中待推断的类型变量。

// 需要获取到 Promise 类型里蕴含的值

type PromiseVal<P> =

P extends Promise<infer INNER> ? INNER : P;

type PStr = Promise<string>;

// Test === string

type Test = PromiseVal<PStr>;

五. 辨别 type & interface

在各大类型库中, 会看到形形色色的 type 和 interface . 然而很多人在实际中却不知道它们的区别.

官网的定义如下:

An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot.

An interface can have multiple merged declarations, but a type alias for an object type literal cannot.

从一张图看出它们两的区别:

建议:  能用 interface 实现,就用 interface , 如果不能才用 type.

为了更好的阅读体验,  《typescrit 最佳实践》

- 欢迎关注「前端加加」,认真学前端,做个有专业的技术人...

Typescript 最佳实践的更多相关文章

  1. Typescript 开发环境的最佳实践

    Typescript 开发环境的最佳实践 0️⃣ git init(略) 1️⃣️️ 初始化:$ yarn add -D ts-node typescript 2️⃣ 生成 tsconfig.json ...

  2. nodejs 实践:express 最佳实践(七) 改造模块 connect2 解析

    nodejs 实践:express 最佳实践(七) 改造模块 connect2 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的 ...

  3. nodejs 实践:express 最佳实践(八) egg.js 框架的优缺点

    nodejs 实践:express 最佳实践(八) egg.js 框架的优缺点 优点 所有的 web开发的点都考虑到了 agent 很有特色 文件夹规划到位 扩展能力优秀 缺点 最大的问题在于: 使用 ...

  4. 中小型前端团队代码规范工程化最佳实践 - ESLint

    前言 There are a thousand Hamlets in a thousand people's eyes. 一千个程序员,就有一千种代码风格.在前端开发中,有几个至今还在争论的代码风格差 ...

  5. 思索 p5.js 的最佳实践

    思索 p5.js 的最佳实践 本文写于 2020 年 12 月 18 日 p5.js 是一个 JavaScript 库,用于为艺术家.设计师提供更容易上手的创意编程. 它有着完整的一套基于 Canva ...

  6. ASP.NET跨平台最佳实践

    前言 八年的坚持敌不过领导的固执,最终还是不得不阔别已经成为我第二语言的C#,转战Java阵营.有过短暂的失落和迷茫,但技术转型真的没有想象中那么难.回头审视,其实单从语言本身来看,C#确实比Java ...

  7. 《AngularJS深度剖析与最佳实践》简介

    由于年末将至,前阵子一直忙于工作的事务,不得已暂停了微信订阅号的更新,我将会在后续的时间里尽快的继续为大家推送更多的博文.毕竟一个人的力量微薄,精力有限,希望大家能理解,仍然能一如既往的关注和支持sh ...

  8. ASP.NET MVC防范CSRF最佳实践

    XSS与CSRF 哈哈,有点标题党,但我保证这篇文章跟别的不太一样. 我认为,网站安全的基础有三块: 防范中间人攻击 防范XSS 防范CSRF 注意,我讲的是基础,如果更高级点的话可以考虑防范机器人刷 ...

  9. 快速web开发中的前后端框架选型最佳实践

    这个最佳实践是我目前人在做的一个站点,主要功能: oauth登录 发布文章(我称为"片段"),片段可以自定义一些和内容有关的指标,如“文中人物:12”.支持自定义排版.插图.建立相 ...

随机推荐

  1. 进入BIOS中,设置U盘启动

    进入BIOS中,一般有system,boot,main,advanced,security等几个选项,main是主设置界面,譬如BIOS时间等等.boot是启动项的设置,我们今天就是要用到它. 找到b ...

  2. React Native错误汇总(持续更新)

    错误1 Element type is invalid-: 错误描述: Element type is invalid: expected a String(for built-in componen ...

  3. Code Force 429B Working out【递推dp】

    Summer is coming! It's time for Iahub and Iahubina to work out, as they both want to look hot at the ...

  4. docker compose安装gitea

    docker-compose.yml version: "3.4" networks: gitea: external: false services: server: image ...

  5. Hbase数据模型 时间戳

  6. HZOI 可怜与超市

    网上搜不着,八成又是哪个学长留下的…… 因为考试第二题我们都好不容易才搞懂,学长有给我们扔了几道类似的题. 其实这道题思路挺好想的,就是一些细节还有复杂度比较难弄,好难调啊. 看到题的第一眼以为是树形 ...

  7. 深度学习的Xavier初始化方法

    在tensorflow中,有一个初始化函数:tf.contrib.layers.variance_scaling_initializer.Tensorflow 官网的介绍为: variance_sca ...

  8. vue3——vue数据循环渲染

    博客地址 :https://www.cnblogs.com/sandraryan/ vue循环渲染 <!DOCTYPE html> <html lang="en" ...

  9. [kuangbin带你飞]专题九 连通图D - Network POJ - 3694

    这道题其实也非常简单,只是在求割边及其个数的情况下,每次往里面加入新的边,并再次计算割边的个数. 我们用tarjan可以求出原图的桥以及个数,当然我们不能暴力加边,然后求解,那么如何求呢??? 其实非 ...

  10. jieba分词流程及部分源码解读(一)

    首先我们来看一下jieba分词的流程图: 结巴中文分词简介 1)支持三种分词模式: 精确模式:将句子最精确的分开,适合文本分析 全模式:句子中所有可以成词的词语都扫描出来,速度快,不能解决歧义 搜索引 ...