信任有些读者现已听说过 DRY 准则,DRY 的全称是 —— Don’t Repeat Yourself ,是指编程进程中不写重复代码,将可以公共的部分笼统出来,封装成东西类或许用笼统类来笼统公共的东西,然后下降代码的耦合性,这样不只进步代码的灵活性、健壮性以及可读性,也便利后期的保护。

接下来,本文将介绍在 TZ ? M I R f M B 6ypeScript 项目开发进程中,怎样参考 DRY 准则2 r D K x & V尽量减少重复代码。减少重复的最简略办法是命名类型,而8 6 U ;不是通过以下这种办法来定义一个 distance 函数:

function distance(a: {x: number, y: number}, b: {x: number, y: number}) {
return Math.sqc f Q - q 6 9 } {rt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
}

在上述的 distance 办法中,咱m Z d d们重复运用 {x: number, yi d r r: number} 来定义参数 a 和参数 b 的类型,要处理这个问题很简略,我们可以定义一个 Pp d s H i c . |oint2D 接口:

interface Point2D {
x: number;
y: number;
}
function distance(a: Point2D, b: Point2D) { /* ... */ }

然而在实践的开发进程中,重复的类型并不总是那么容易N 5 J被发现。有时它们会被语法所掩盖。假如多个函数同享相同的类型签名,比如:

function get(url: string, opts: Options): Promise<Response> { /* ... */ }
function post(url: string,@ U ] D I h ! opts: Options): ProQ I / & n U #mise<Response> { /* ... */ }

关于上面的 get 和 post 办B c q ! X / x法,为了避免重复的代码,我们可以z G P g : n &提取统一的类型签名:

type HTTPFunction = (url: string, opts: Options) => Promise<Response>;
const get: HTTPFunction = (url, opts) => { /*a Z ? u - ( / H ... */ };
const post: HTTPFunction = (url, opts) => { /* ... */1 a T w A % Z };

关于 TypeSP ( : _ . j ` Q pcript 初学者来说,在定义接口的时候也要当心,避免出现以下类似的重复代码。比如:

inteM L 0 c ` : G / _rface Person {
firstName: string;
lastName: string;
}
interface PersonWithBirthDate {
firstName: string;
lastName: strinQ 7 ~g;
birth: Date;: q C
}

很明显,相关于 Person 接口来说,PersonWithBirthDate 接口只是多了一个 birth 特色,其他的G 1 ] ( & 0 ~ e &特色跟 Person 接口是相同的。那么怎样避免出现比如中的重复代码呢?要处( _ i I ` M C a ,理这个问题,可以运用 extends 关键字:

interface Person {
firstName: string;
lastName: string;
}
interface PersonWithBirthDate exten3 T P  T Lds Person {
birth: Date;
}

当然除了运用 extends 关键字之外,也可以运用穿插运算符(&am6 _ – ? = dp;):

type PersonWithBirthDate = Perso_ ? P R c , @n & { birth: Dp . - K tate };

下面我们来持续看另一个比如,假设你现已定义 State(代表整个应用程序的状况)和 TopNavState(只代表部分应用程序的状况)两个接口:

interface State {
userId: str5 # ~ing;
pageTitle: string;
recentFiles: string[];
pageContents: string;
}
interface TopNavState {
userId: string;
pageTitle:D 8 ] m 7 d o + string;
r: a # ] M KecentFiles: string[];P ] z 0
}

上述的 TopNavState 接口比较 State 接口只是缺少了 pageContents 特色,但我们却重复声明其他三个相同的特色。为了减少重复代码,我们可以这样做:

type TopNavState = {
userId: State['userId'];
pageTitle: State['pageTitle'];
recentFiles: State['recenC N 4 [tFiles'];
};

在上面代码中,我们通过成员访问的语法来提取方针中特色的类型,然后避免重复定义接口中相关特色的类型。但这并没有+ h M 3 _处理本质的问题,我们还有很大的优化空间。针对这个问题,我们可以运用映射类型来进一 3 I步做优化:

type TopNavState = {
[k in 'userId' | 'pageTitF x %le' | 'recentFiles']: State[k]
};

鼠标悬停在 TopNavState 显现它的声明,实践上,这个定义与前一个定义完全相同。

在 TS 中怎么减少重复代码

r 7 j过映射类型优化后的代码,比较 TopNavState 接口开始的代码简练了许多。那还有没有优化空间呢?其实是有的,我们可以运; 9 | 2 !用 TypeScf C , jript 团队为我们开发者供给的东西类型,这儿我们可以运用 Pick

type TopNavState = Pi6 3  ;ck<
State, 'A { X 1 ( W k / |usG 4 w l herId' | 'pageTitle' | 'recentFiles'
>;

其实除了 Pick 之外,在实践开发进程U (我们还可以运用其他内置的东西类型来削= ` | g 7减重复代码。o ( B u ^ W这儿我们再来介绍另一个比较常用的东西类型,即 Partial。以下是未运用 Partial 的比如:

interface Options {
width: number;
height: number;
color: stry . E 8 Jing;
label: string;
}
interface OptionsUpdate {
width?: number;
heigC Y wht?: number;
color?: string;
label?: string;
}
class UIWidget {
constructor(init: Options) {
/* ... */
}
update(options: OptionsUpdate [ u Ge) {
/* ... */
}
}

在以上示例中,我们定义了 Options 和 OptionsUpdate 两个接口,它们分别用于描绘 UIWidget 的初始化配备项和更新配备项。比较初始化配备项,更新配备项的全部特色都是可选的。

R . Z . : H k在我们来开始优化上述的代码,我们先来看一下不运用 Pq X 8 9artial 的景象:

type OptionsUpdate = {[k in keyof Options]?: Opti/ # d @ tonsL G $ C Z ;[k]};

keyof 操作符接受一个类型,并回来一个由 key 组成的联合类型:

type OptionsKeys = keyof O# $ Z Dptionsd T H x h X;
// Type is "width" | "height" | "color" | "label"

. 8 c P g R _ in 操作符是用来遍历枚举类型或联合类型。接着,我们来看一下运用 Partial 的景象:

cla+ g g e ) * #ss UIWidget {
co= ^ I = M Lnstructor(init: Options) { /* ... */a w N ^ / ~ g e & }
update(options: Partial<Options>) { /* ... */ }
}

其实 Partial 并没有什么奇特的当地,我们来看一下它的定义:

// node_modulesT ) t K 4 % /typescript/lib/lib.es5.d.ts
/**
*3 t n Make all properties: * f _ 2 m T F Z in T optional
*/
type Part` = ] 4 k 9ial<T> = {
[P in keyof T]?: T[P];
};

在以上代码中,首要通过 keyof TO v n o 拿到 T 的全部特色名,然后运用 in 进行遍历,将值赋给 P,最终通过 T[P] 取得相应的; – p |特色类型。中心的 ? 号,用于将全部特色变为可选。

有时候,你或_ R R ~ i 7许还会发现自己想要定义一个类型来匹配一个初始配备项的形状,比如:

const INIT_OPTIONS =j S ) r f E {
width: 640,
height: 480,
color: "#00FF00",
label: "VGA",
};
interface Options {
width:q x 2 number;
height: number;
color: string;
label: string;
}

关于 Options 接口来说,我们还可以运用 typeof 操作符来快速定义该接口类型:

type Options[ 1 . = typeof INIT_OPTIONS;

此外,在运用可辨识联合(代数数据类型或标签联Y } o q ) % F合类型)的进程中,也或许出现重复代码。比如:

interface SaveAction {
type: 'save';
// ...
}
interface LoadAction {
type: 'load';
//: M D G s ...
}
type Action = SaveAction | LoadAction;
type ActionType = 'save'G i i | 'load'; // Repeated tK ^ lypes!

为了避免重复定义 'save''load',我们能# | b 7 I够运用前面说到的成员访问语法,来提取方针中特色的Z 7 U h r x类型:

type ActionType = Action['type']; // 类型是 "save" | "load"

这儿O C p A需要留心的是,Ac0 { ] M ^ D _ Dtion['tf g p ?ype'] 回来的是联合类型,而假如我们运用前面介绍的 Pick 东西类I – v Y b J K V型,它会回来一个含有 ty, 4 J – gpel d I 6 u t 特色的接口:

type ActionRec = Pick<Action, 'type'>; // {type: "save" | "load"}

本文通过一些简略的示h . b G – 0 ! n例,介绍了在 TypeScript 开发进程中怎样减少重复代码,其实除了文中介绍了 PickPartial 之外,TypeScript 团队还为我们开发者供给了很多东西类型,可用于减少重复代码和进步开发功率,感兴趣的读者可以阅读本人之前写的 掌握 TS 这些东西类型,让你开发事半功倍H H 0 9 这篇C O F u L ^文章。

创建了一个 “重学TyQ 8 !peScript” 的微信群C ? ) P [ W B A o,想加群的小伙伴,加我微信 “semlinker”,备注重学TS。现在已有 TS 系列文章 38 篇。

在 TS 中怎么减少重复代码