关于前端:掌握-TypeScript20-个提高代码质量的最佳实践

35次阅读

共计 9657 个字符,预计需要花费 25 分钟才能阅读完成。

本文首发于微信公众号:大迁世界, 我的微信:qq449245884,我会第一工夫和你分享前端行业趋势,学习路径等等。
更多开源作品请看 GitHub https://github.com/qq449245884/xiaozhi,蕴含一线大厂面试残缺考点、材料以及我的系列文章。

介绍

TypeScript 是一种宽泛应用的开源编程语言,非常适合现代化开发。借助它先进的类型零碎,TypeScript 容许开发者编写更加健壮、可保护和可扩大的代码。然而,要真正施展 TypeScript 的威力并构建高质量的我的项目,理解和遵循最佳实际至关重要。在本文中,咱们将深刻摸索 TypeScript 的世界,并探讨把握该语言的 21 个最佳实际。这些最佳实际涵盖了各种主题,并提供了如何在实在我的项目中利用它们的具体示例。无论你是初学者还是经验丰富的 TypeScript 开发者,本文都将提供有价值的见解和技巧,帮忙你编写洁净高效的代码。

最佳实际 1:严格的类型查看

咱们将从最根本的实际开始。设想一下,在问题呈现之前就能发现潜在谬误,听起来太好不过了吧?这正是 TypeScript 中严格类型查看所能为你做到的。这个最佳实际的目标是捕获那些可能会轻轻溜进你的代码并在前面引发麻烦的虫子。

严格类型查看的次要作用是确保你的变量类型与你冀望的类型匹配。这意味着,如果你申明一个变量为字符串类型,TypeScript 将确保调配给该变量的值的确是字符串而不是数字,例如。这有助于您及早发现错误,并确保您的代码依照预期工作。

启用严格类型查看只需在 tsconfig.json 文件中增加 "strict": true(默认为 true)即可。通过这样做,TypeScript 将启用一组查看,以捕捉某些本应未被发现的谬误。

以下是一个例子,阐明严格类型查看如何能够帮忙你防止常见谬误:

let userName: string = "John";
userName = 123; // TypeScript will raise an error because "123" is not a string.

通过遵循这个最佳实际,你将可能及早发现错误,并确保你的代码依照预期工作,从而为你节省时间和不必要的麻烦。

最佳实际 2:类型推断

TypeScript 的核心理念是显式地指定类型,但这并不意味着你必须在每次申明变量时都明确指定类型。

类型推断是 TypeScript 编译器依据变量赋值的值主动推断变量类型的能力。这意味着你不用在每次申明变量时都显式指定类型。相同,编译器会依据值推断类型。

例如,在以下代码片段中,TypeScript 会主动推断 name 变量的类型为字符串:

let name = "John";

类型推断在解决简单类型或将变量初始化为从函数返回的值时特地有用。

然而请记住,类型推断并不是一个魔法棒,有时候最好还是显式指定类型,特地是在解决简单类型或确保应用特定类型时。

最佳实际 3:应用 Linters

Linters 是一种能够通过强制一组规定和指南来帮忙你编写更好代码的工具。它们能够帮忙你捕获潜在的谬误,进步代码的整体品质。

有几个针对 TypeScript 的 Linters 可供选择,例如 TSLint 和 ESLint,能够帮忙你强制执行统一的代码格调并捕获潜在的谬误。这些 Linters 能够配置查看诸如短少分号、未应用的变量和其余常见问题等事项。

最佳实际 4:应用接口

当波及到编写洁净、可保护的代码时,接口是你的好敌人。它们就像是对象的蓝图,概述了你将要应用的数据的构造和属性。

在 TypeScript 中,接口定义了对象的形态的约定。它指定了该类型的对象应具备的属性和办法,并且能够用作变量的类型。这意味着,当你将一个对象调配给带有接口类型的变量时,TypeScript 会查看对象是否具备接口中指定的所有属性和办法。

以下是 TypeScript 中定义和应用接口的示例:

interface User {
name: string;
age: number;
}
let user: User = {name: "John", age: 25};

接口还能够使代码重构更容易,因为它确保了应用某个特定类型的所有地位都会被一次性更新。

最佳实际 5:类型别名

TypeScript 容许你应用类型别名(type aliases)创立自定义类型。类型别名和接口(interface)的次要区别在于,类型别名为类型创立一个新名称,而接口为对象的形态创立一个新名称。

例如,你能够应用类型别名为二维空间中的点创立一个自定义类型:

type Point = {x: number, y: number};
let point: Point = {x: 0, y: 0};

类型别名也能够用于创立简单类型,例如联结类型(union type)或穿插类型(intersection type)。

type User = {name: string, age: number};
type Admin = {name: string, age: number, privileges: string[] };
type SuperUser = User & Admin;

最佳实际 6:应用元组

元组是一种示意具备不同类型的固定大小元素数组的形式。它们容许你用特定的程序和类型示意值的汇合。

例如,你能够应用元组来示意二维空间中的一个点:

let point: [number, number] = [1, 2];

你还能够应用元组来示意多个类型的汇合:

let user: [string, number, boolean] = ["Bob", 25, true];

应用元组的次要劣势之一是,它们提供了一种在汇合中表白特定类型关系的形式。

此外,你能够应用解构赋值来提取元组的元素并将它们调配给变量:

let point: [number, number] = [1, 2];
let [x, y] = point;
console.log(x, y);

最佳实际 7:应用 any 类型

有时,咱们可能没有无关变量类型的所有信息,但依然须要在代码中应用它。在这种状况下,咱们能够利用 any 类型。然而,像任何弱小的工具一样,应用 any 应该审慎和有目的地应用。

应用 any 的一个最佳实际是将其应用限度在真正未知类型的特定状况下,例如在应用第三方库或动静生成的数据时。此外,最好增加类型断言或类型爱护,以确保变量被正确应用。尽可能放大变量类型的范畴。

例如:

function logData(data: any) {console.log(data);
}

const user = {name: "John", age: 30};
const numbers = [1, 2, 3];

logData(user); // {name: "John", age: 30}
logData(numbers); // [1, 2, 3]

另一个最佳实际是防止在函数返回类型和函数参数中应用 any,因为它可能会减弱代码的类型安全性。相同,你能够应用更具体的类型或应用一些提供肯定水平类型平安的更通用的类型,如 unknown 或 object。

最佳实际 8:应用 unknown 类型

unknown 类型是 TypeScript 3.0 中引入的一种弱小且限制性更强的类型。它比 any 类型更具限制性,并能够帮忙你避免意外的类型谬误。

any 不同的是,当你应用 unknown 类型时,除非你首先查看其类型,否则 TypeScript 不容许你对值执行任何操作。这能够帮忙你在编译时捕捉到类型谬误,而不是在运行时。

例如,你能够应用 unknown 类型创立一个更加类型平安的函数:

function printValue(value: unknown) {if (typeof value === "string") {console.log(value);
 } else {console.log("Not a string");
 }
}

你也能够应用 unknown 类型创立更加类型平安的变量:

let value: unknown = "hello";
let str: string = value; // Error: Type 'unknown' is not assignable to type 'string'.

最佳实际 9:“never”

在 TypeScript 中,never 是一个非凡的类型,示意永远不会产生的值。它用于批示函数不会失常返回,而是会抛出谬误。这是一种很好的形式,能够向其余开发人员(和编译器)批示一个函数不能以某种形式应用,这能够帮忙捕获潜在的谬误。

例如,思考以下函数,如果输出小于 0,则会抛出谬误:

function divide(numerator: number, denominator: number): number {if (denominator === 0) {throw new Error("Cannot divide by zero");
 }
 return numerator / denominator;
}

这里,函数 divide 申明为返回一个数字,但如果分母为零,则会抛出谬误。为了批示在这种状况下该函数不会失常返回,你能够应用 never 作为返回类型:

function divide(numerator: number, denominator: number): number | never {if (denominator === 0) {throw new Error("Cannot divide by zero");
 }
 return numerator / denominator;
}

最佳实际 10:应用 keyof 运算符

keyof 运算符是 TypeScript 的一个弱小性能,能够创立一个示意对象键的类型。它能够用于明确批示哪些属性是对象容许的。

例如,你能够应用 keyof 运算符为对象创立更可读和可保护的类型:

interface User {
 name: string;
 age: number;
}
type UserKeys = keyof User; // "name" | "age"

你还能够应用 keyof 运算符创立更加类型平安的函数,将对象和键作为参数:

function getProperty<T, K extends keyof T>(obj: T, key: K) {return obj[key];
}

这将容许你在编译时查看 key 是否为对象 T 的键之一,并返回该键对应的值。

function getValue<T, K extends keyof T>(obj: T, key: K) {return obj[key];
}
let user: User = {name: "John", age: 30};
console.log(getValue(user, "name")); // "John"
console.log(getValue(user, "gender")); // Error: Argument of type '"gender"' is not assignable to parameter of type '"name" | "age"'.

最佳实际 11:应用枚举

枚举(Enums)是 TypeScript 中定义一组命名常量的一种形式。它们能够用于创立更具可读性和可维护性的代码,通过给一组相干的值赋予有意义的名称。

例如,你能够应用枚举来定义一个订单可能的状态值:

enum OrderStatus {
  Pending,
  Processing,
  Shipped,
  Delivered,
  Cancelled
}
let orderStatus: OrderStatus = OrderStatus.Pending;

枚举还能够有自定义的一组数字值或字符串值:

enum OrderStatus {
  Pending = 1,
  Processing = 2,
  Shipped = 3,
  Delivered = 4,
  Cancelled = 5
}
let orderStatus: OrderStatus = OrderStatus.Pending;

在命名约定方面,枚举应该以第一个大写字母命名,并且名称应该是复数模式。

最佳实际 12:应用命名空间

命名空间(Namespaces)是一种组织代码和避免命名抵触的办法。它们容许你创立一个容器来定义变量、类、函数和接口。

例如,你能够应用命名空间来将所有与特定性能相干的代码分组:

namespace OrderModule {export class Order { /* … /}
  export function cancelOrder(order: Order) {/ … /}
  export function processOrder(order: Order) {/ … */}
}
let order = new OrderModule.Order();
OrderModule.cancelOrder(order);

你也能够应用命名空间来为你的代码提供一个独特的名称,以避免命名抵触:

namespace MyCompany.MyModule {export class MyClass { /* … */}
}
let myClass = new MyCompany.MyModule.MyClass();

须要留神的是,命名空间相似于模块,但它们用于组织代码和避免命名抵触,而模块用于加载和执行代码。

最佳实际 13:应用实用类型

实用类型(Utility Types)是 TypeScript 中内置的一种个性,提供了一组预约义类型,能够帮忙你编写更好的类型平安代码。它们容许你执行常见的类型操作,并以更不便的形式操作类型。

例如,你能够应用 Pick 实用类型从对象类型中提取一组属性:

type User = {name: string, age: number, email: string};
type UserInfo = Pick<User, "name" | "email">;

你也能够应用 Exclude 实用类型从对象类型中删除属性:

type User = {name: string, age: number, email: string};
type UserWithoutAge = Exclude<User, "age">;

你能够应用 Partial 实用类型将类型的所有属性设置为可选的:

type User = {name: string, age: number, email: string};
type PartialUser = Partial<User>;

除了上述实用类型外,还有许多其余实用类型,如 Readonly、Record、Omit、Required 等,能够帮忙你编写更好的类型平安代码。

最佳实际 14:“只读”和“只读数组”

当在 TypeScript 中解决数据时,你可能心愿确保某些值无奈更改。这就是“只读”和“只读数组”的用武之地。

“只读”关键字用于使对象的属性只读,意味着在创立后它们无奈被批改。例如,在解决配置或常量值时,这十分有用。

interface Point {
  x: number;
  y: number;
}
let point: Readonly<Point> = {x: 0, y: 0};
point.x = 1; // TypeScript 会报错,因为“point.x”是只读的 

“只读数组”与“只读”相似,然而用于数组。它使一个数组变成只读状态,在创立后不能被批改。

let numbers: ReadonlyArray<number> = [1, 2, 3];
numbers.push(4); // TypeScript 会报错,因为“numbers”是只读的 

最佳实际 15: 类型爱护

在 TypeScript 中,解决简单类型时,很难跟踪变量的不同可能性。类型爱护是一种弱小的工具,能够依据特定条件放大变量的类型范畴。

以下是如何应用类型爱护查看变量是否为数字的示例:


function isNumber(x: any): x is number {return typeof x === "number";}
let value = 3;
if (isNumber(value)) {value.toFixed(2); // TypeScript 晓得 "value" 是一个数字,因为有了类型爱护
}

类型爱护还能够与“in”运算符、typeof 运算符和 instanceof 运算符一起应用。

最佳实际 16:应用泛型

泛型是 TypeScript 的一个弱小个性,能够让你编写能够与任何类型一起应用的代码,从而使其更具备可重用性。泛型容许你编写一个独自的函数、类或接口,能够与多种类型一起应用,而不用为每种类型编写独自的实现。

例如,你能够应用泛型函数来创立任何类型的数组:

function createArray<T>(length: number, value: T): Array<T> {let result = [];
  for (let i = 0; i < length; i++) {result[i] = value;
  }
  return result;
}
let names = createArray<string>(3, "Bob");
let numbers = createArray<number>(3, 0);

你也能够应用泛型来创立一个能够解决任何类型数据的类:

class GenericNumber<T> {
 zeroValue: T;
 add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {return x + y;};

最佳实际 17:应用 infer 关键字

infer 关键字是 TypeScript 的一个弱小个性,它容许你从一个类型中提取出变量的类型。

例如,你能够应用 infer 关键字为返回特定类型数组的函数创立更准确的类型:

type ArrayType<T> = T extends (infer U)[] ? U : never;
type MyArray = ArrayType<string[]>; // MyArray 类型是 string

你也能够应用 infer 关键字为返回具备特定属性的对象的函数创立更准确的类型:

type Person = {name: string, age: number};
type PersonName = keyof Person;
type PersonProperty<T> = T extends {[K in keyof T]: infer U } ? U : never;
type Name = PersonProperty<Person>;

在下面的例子中,咱们应用了 infer 关键字来提取出对象的属性类型,这个技巧能够用于创立更精确的类型定义。

type ObjectType<T> = T extends {[key: string]: infer U } ? U : never;
type MyObject = ObjectType<{name: string, age: number}>; // MyObject is of type {name:string, age: number}

最佳实际 18:应用条件类型

条件类型容许咱们表白更简单的类型关系。基于其余类型的条件创立新类型。

例如,能够应用条件类型来提取函数的返回类型:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type R1 = ReturnType<() => string>; // string
type R2 = ReturnType<() => void>; // void

还能够应用条件类型来提取对象类型的属性,满足特定条件:

type PickProperties<T, U> = {[K in keyof T]: T[K] extends U ? K : never }[keyof T];
type P1 = PickProperties<{a: number, b: string, c: boolean}, string | number>; // "a" | "b"

最佳实际 19:应用映射类型

映射类型是一种基于现有类型创立新类型的形式。通过对现有类型的属性利用一组操作来创立新类型。

例如,能够应用映射类型创立一个示意现有类型只读版本的新类型:

type Readonly<T> = {readonly [P in keyof T]: T[P] };
let obj: {a: number, b: string} = {a: 1, b: "hello"};
let readonlyObj: Readonly<typeof obj> = {a: 1, b: "hello"};

还能够应用映射类型创立一个示意现有类型可选版本的新类型:

type Optional<T> = {[P in keyof T]?: T[P] };
let obj: {a: number, b: string} = {a: 1, b: "hello"};
let optionalObj: Optional<typeof obj> = {a: 1};

映射类型能够以不同的形式应用:创立新类型、从现有类型中增加或删除属性,或更改现有类型的属性类型。

最佳实际 20:应用装璜器

装璜器是一种应用简略语法来为类、办法或属性增加额定性能的形式。它们是一种加强类的行为而不批改其实现的形式。

例如,能够应用装璜器为办法增加日志记录:

  function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    let originalMethod = descriptor.value;
    descriptor.value = function(…args: any[]) {console.log(Calling ${propertyKey} with args: ${JSON.stringify(args)});
    let result = originalMethod.apply(this, args);
    console.log(Called ${propertyKey}, result: ${result});
    return result;
  }
}
class Calculator {
  @logMethod
  add(x: number, y: number): number {return x + y;}
}

还能够应用装璜器为类、办法或属性增加元数据,这些元数据能够在运行时应用。

function setApiPath(path: string) {return function (target: any) {target.prototype.apiPath = path;}
}
@setApiPath("/users")
class UserService {// …}
console.log(new UserService().apiPath); // "/users"

总结

本文次要介绍了 TypeScript 的 20 个最佳实际,旨在进步代码品质和开发效率。其中,一些最佳实际包含尽可能应用 TypeScript 的类型零碎、应用函数和办法参数默认值、应用可选链操作符等。此外,该文章还强调了在应用类时,应该应用拜访修饰符,以避免出现不必要的谬误。

该文章指出,应用 TypeScript 的类型零碎能够帮忙开发人员防止一些常见的谬误,例如在运行时引发异样。此外,还提供了一些对于如何编写类型正文的最佳实际。例如,应该尽可能应用函数和办法参数默认值,以防止参数为空或未定义时的谬误。

文章中还介绍了一些如何应用 TypeScript 的高级个性的最佳实际,例如应用类型别名和枚举,以进步代码的可读性和可维护性。此外,该文章还强调了如何应用可选链操作符来防止一些运行时谬误。

总之,该文章提供了许多有用的 TypeScript 最佳实际,这些实际能够帮忙开发人员编写更高质量的代码,进步开发效率,防止一些常见的谬误。

代码部署后可能存在的 BUG 没法实时晓得,预先为了解决这些 BUG,花了大量的工夫进行 log 调试,这边顺便给大家举荐一个好用的 BUG 监控工具 Fundebug。


原文:https://itnext.io/mastering-typescript-21-best-practices-for-…

交换

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

正文完
 0