TypeScript的简洁代码-第2部分

从评论的角度来看, 第一部分引起了不同意见,尤其是在枚举部分方面。 无论是原著者还是评论者,我在某些地方也都不同意。 但是,正如在第一部分的初始描述中所指出的那样,干净的代码不是要遵循的教条,而只是建议,每个人都选择遵守它们的要求以适应他们的需求和观点。



对象和数据结构


使用免疫


TypeScript中的类型系统使您可以将接口/类的各个属性标记为只读字段(只读) 。 这使您可以正常工作(意外的突变是不好的)。 对于更复杂的场景,有一个内置的Readonly类型,该类型采用T类型并使用映射类型(请参阅映射类型 )标记其所有只读属性。


不好:


 interface Config { host: string; port: string; db: string; } 

好:


 interface Config { readonly host: string; readonly port: string; readonly db: string; } 

对于数组,可以使用ReadonlyArray<T>创建一个只读数组。 不允许使用push()fill()进行更改,但是可以使用concat()slice()来更改值。


不好:


 const array: number[] = [ 1, 3, 5 ]; array = []; // error array.push(100); // array will updated 

好:


 const array: ReadonlyArray<number> = [ 1, 3, 5 ]; array = []; // error array.push(100); // error 

声明只读参数TypeScript 3.4稍微容易一些


 function hoge(args: readonly string[]) { args.push(1); // error } 

文字值的首选const断言


不好:


 const config = { hello: 'world' }; config.hello = 'world'; //   const array = [ 1, 3, 5 ]; array[0] = 10; //   //    function readonlyData(value: number) { return { value }; } const result = readonlyData(100); result.value = 200; //   

好:


 //     const config = { hello: 'world' } as const; config.hello = 'world'; //  //     const array = [ 1, 3, 5 ] as const; array[0] = 10; //  //        function readonlyData(value: number) { return { value } as const; } const result = readonlyData(100); result.value = 200; //  

类型与 介面


在可能需要联合或交叉时使用类型。 当您要使用extendsimplements时,请使用该接口。 但是,不存在严格的规则,请使用适合您的方法。 有关更详细的说明,请参见有关TypeScript中typeinterface之间差异的这些答案


不好:


 interface EmailConfig { // ... } interface DbConfig { // ... } interface Config { // ... } //... type Shape = { // ... } 

好:


 type EmailConfig = { // ... } type DbConfig = { // ... } type Config = EmailConfig | DbConfig; // ... interface Shape { // ... } class Circle implements Shape { // ... } class Square implements Shape { // ... } 

班级


班级应小


一个班级的规模由其职责来衡量。 遵循全责原则,班级应小。


不好:


 class Dashboard { getLanguage(): string { /* ... */ } setLanguage(language: string): void { /* ... */ } showProgress(): void { /* ... */ } hideProgress(): void { /* ... */ } isDirty(): boolean { /* ... */ } disable(): void { /* ... */ } enable(): void { /* ... */ } addSubscription(subscription: Subscription): void { /* ... */ } removeSubscription(subscription: Subscription): void { /* ... */ } addUser(user: User): void { /* ... */ } removeUser(user: User): void { /* ... */ } goToHomePage(): void { /* ... */ } updateProfile(details: UserDetails): void { /* ... */ } getVersion(): string { /* ... */ } // ... } 

好:


 class Dashboard { disable(): void { /* ... */ } enable(): void { /* ... */ } getVersion(): string { /* ... */ } } //  ,       // ... 

高内聚力低粘结


内聚性决定了班级成员之间相互关联的程度。 理想情况下,类中的所有字段都应由每种方法使用。 我们说班级尽可能地联系在一起 。 然而实际上,这并不总是可能的,甚至是不希望的。 但是,您必须确保凝聚力很高。


连接性还指两个类如何相互关联或相互依赖。 如果一个类的更改不影响另一个类,则认为它们是松散耦合的。


不好:


 class UserManager { // :         . //   ,    ,    //      ,     , //       `emailSender`. constructor( private readonly db: Database, private readonly emailSender: EmailSender) { } async getUser(id: number): Promise<User> { return await db.users.findOne({ id }); } async getTransactions(userId: number): Promise<Transaction[]> { return await db.transactions.find({ userId }); } async sendGreeting(): Promise<void> { await emailSender.send('Welcome!'); } async sendNotification(text: string): Promise<void> { await emailSender.send(text); } async sendNewsletter(): Promise<void> { // ... } } 

好:


 class UserService { constructor(private readonly db: Database) { } async getUser(id: number): Promise<User> { return await this.db.users.findOne({ id }); } async getTransactions(userId: number): Promise<Transaction[]> { return await this.db.transactions.find({ userId }); } } class UserNotifier { constructor(private readonly emailSender: EmailSender) { } async sendGreeting(): Promise<void> { await this.emailSender.send('Welcome!'); } async sendNotification(text: string): Promise<void> { await this.emailSender.send(text); } async sendNewsletter(): Promise<void> { // ... } } 

优先考虑组成而不是继承


正如《第四帮的设计模式》所述,您必须
在任何可能的地方,都优选组合而不是继承 。 有很多使用继承的好理由,也有很多使用合成的好理由。 该原则的实质是,如果您的思维本能地继续继承,请尝试考虑这种组合是否可以更好地为您的问题建模。 在某些情况下可以。


然后您可能会问:“我什么时候应该使用继承?” 这取决于您的问题,但是当继承比组成更有意义时,这是一个不错的清单:


  1. 您的继承关系是“是”关系,而不是“是”关系(人类->动物与用户->用户详细信息)。
  2. 您可以重用基类中的代码(人们可以像所有动物一样移动)。
  3. 您想通过更改基类对派生类进行全局更改。 (移动它们时所有动物的卡路里消耗发生变化)。

不好:


 class Employee { constructor( private readonly name: string, private readonly email: string) { } // ... } // ,   Employees ""  . EmployeeTaxData    Employee class EmployeeTaxData extends Employee { constructor( name: string, email: string, private readonly ssn: string, private readonly salary: number) { super(name, email); } // ... } 

好:


 class Employee { private taxData: EmployeeTaxData; constructor( private readonly name: string, private readonly email: string) { } setTaxData(ssn: string, salary: number): Employee { this.taxData = new EmployeeTaxData(ssn, salary); return this; } // ... } class EmployeeTaxData { constructor( public readonly ssn: string, public readonly salary: number) { } // ... } 

使用通话链


这种模式非常有用,并在许多库中普遍使用。 这样可以使您的代码更具表现力,并且不再那么冗长。 因此,请使用一系列方法,并查看代码的干净程度。


不好:


 class QueryBuilder { private collection: string; private pageNumber: number = 1; private itemsPerPage: number = 100; private orderByFields: string[] = []; from(collection: string): void { this.collection = collection; } page(number: number, itemsPerPage: number = 100): void { this.pageNumber = number; this.itemsPerPage = itemsPerPage; } orderBy(...fields: string[]): void { this.orderByFields = fields; } build(): Query { // ... } } // ... const queryBuilder = new QueryBuilder(); queryBuilder.from('users'); queryBuilder.page(1, 100); queryBuilder.orderBy('firstName', 'lastName'); const query = queryBuilder.build(); 

好:


 class QueryBuilder { private collection: string; private pageNumber: number = 1; private itemsPerPage: number = 100; private orderByFields: string[] = []; from(collection: string): this { this.collection = collection; return this; } page(number: number, itemsPerPage: number = 100): this { this.pageNumber = number; this.itemsPerPage = itemsPerPage; return this; } orderBy(...fields: string[]): this { this.orderByFields = fields; return this; } build(): Query { // ... } } // ... const query = new QueryBuilder() .from('users') .page(1, 100) .orderBy('firstName', 'lastName') .build(); 

待续...

Source: https://habr.com/ru/post/zh-CN485068/


All Articles