内容简介:然而现在的我想说: 真香.我们经常吐槽别人代码可维护性特别低, 总是希望别人能够主动的写注释, 可是写注释却没有任何方式可以进行约束. 这下好了, 类型就是最好的注释, 用
一年前刚接触 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 Chining
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
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 extendsPromise < 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.
- END -
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
-
点个「 在看 」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)
-
关注我的官网 https:// m uyiy.cn ,让我们成为长期关系
-
关注公众号「 高级前端进阶 」,每周重点攻克一个前端面试重难点,公众号后台回复「面试题」 送你高级前端面试题。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 渗透技巧之Powershell实战技巧
- 渗透技巧——快捷方式文件的参数隐藏技巧
- Python实用技巧,你不知道的7个好玩的Python技巧
- Python 技巧总结
- 监控OpenStack的技巧
- JNI技巧
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。