vlambda博客
学习文章列表

你应该掌握的 5 大 TypeScript 功能

英文 | https://betterprogramming.pub/top-5-typescript-features-you-should-master-2358db9ab3d5

翻译 | 杨小二


TypeScript 的影响力与日俱增。它现在是任何新的 Web/Node 项目的首选配套工具。使用 TypeScript 的好处怎么强调都不为过。然而,了解和理解这个 JavaScript 超集拥有的所有工具是很重要的。

你是否正在投入时间来提高你的TypeScript技能?你想充分利用它吗?有时,由于没有使用正确的 TypeScript 功能并且没有遵循其最佳实践,可能会出现大量代码重复和样板。

在本文中,我们将研究 TypeScript 可以赋予我们的五个最重要的功能。通过确保并了解它们的用例,我们可以构建更好、更全面的代码库。

1、Unions

联合是最基本且易于使用的 TypeScript 功能之一。它们让我们可以轻松地将多种类型合二为一。交集和联合类型是我们组合类型的方法之一。

function logIdentifier(id: string | number) { console.log('id', id);}

当我们想要表示某个类型可以为空时,它们非常有用:

function logIdentifier(id: string | undefined) { if(!id) { console.error('no identifier found'); } else { console.log('id', id); }}

不仅限于未定义或原语。它们可用于任何接口或类型。

interface Vehicle { speed: number;}interface Bike extends Vehicle { ride: () => void;}interface Plane extends Vehicle { fly: () => void;}function useVehicle(vehicle: Bike | Plane) { ...}

鉴于上面的联合类型,我们如何区分自行车和飞机?通过使用可区分联合功能。我们将创建一个名为 Vehicles 的枚举并将其用作属性值。

看看代码如何:

enum Vehicles { bike, plane}
interface Vehicle { speed: number; type: Vehicles;}
interface Bike extends Vehicle { ride: () => void; type: Vehicles.bike;}
interface Plane extends Vehicle { fly: () => void; type: Vehicles.plane;}
function useVehicle(vehicle: Bike | Plane) { if (vehicle.type === Vehicles.bike) { vehicle.ride(); }
if (vehicle.type === Vehicles.plane) { vehicle.fly(); }}

从而,我们可以看到Unions是一个简单而强大的工具,它有一些技巧。但是,如果我们想以更强大和动态的方式表达类型/接口,我们需要使用泛型。

2、泛型

使我们的方法/API 可重用的最佳方法是什么?泛型! 这是大多数类型语言中的一项功能。它让我们以更通用的方式表达类型。这将赋予我们的类和类型。

让我们从一个基本的例子开始。让我们创建一个方法来将任何定义的类型添加到数组中:

function addItem(item: string, array: string[]) { array = [...array, item]; return array;}

如果我们想为 int 类型创建相同的实用程序怎么办?我们应该重做同样的方法吗?通过简单地使用泛型,我们可以重用代码而不是添加更多样板:

function addItem<T>(item: T, array: T[]) { array = [...array, item]; return array;}
addItem('hello', []);
addItem(true, [true, true]);

我们如何防止在 T 中使用不需要的类型?为此,我们可以使用 extends 关键字:

function addItem<T extends boolean | string>(item: T, array: T[]) { array = [...array, item]; return array;}
addItem('hello', []);
addItem(true, [true, true]);
addItem(new Date(), []);// ^^^^^^^^^^// Argument of type 'Date' is not assignable to parameter of type 'string | boolean'

泛型将使我们能够为我们的类型构建全面和动态的接口。它们是必须掌握的功能,需要在我们的日常开发中出现。

3、元组

什么是元组?我们来看看定义:

元组类型允许你用固定数量的元素来表达数组,这些元素的类型是已知的,但不必相同。例如,你可能希望将一个值表示为一对字符串和一个数字。” 

——TypeScript 的文档

最重要的一点是这些数组的值长度是固定的。定义元组有两种方式:

明确:

const array: [string, number] = ['test', 12];

隐含地:

const array = ['test', 12] as const;

唯一的区别是 as const 将使数组只读,这在我看来是可取的。

请注意,元组也可以被标记:

function foo(x: [startIndex: number, endIndex: number]) { ...}

标签不需要我们在解构时以不同的方式命名我们的变量。它们纯粹是为了文档和工具。标签将有助于使我们的代码更具可读性和可维护性。

请注意,使用标记元组时有一个重要规则:标记元组元素时,元组中的所有其他元素也必须被标记。

4、映射类型

什么是映射类型?它们是一种避免反复定义接口的方法。你可以将类型建立在另一种类型或接口的基础上,从而节省手动工作。

“当你不想重复时,有时一种类型需要基于另一种类型。映射类型建立在索引签名的语法之上,用于声明尚未提前声明的属性类型。” — TypeScript 的文档

总而言之,映射类型允许我们基于现有类型创建新类型。

TypeScript 确实附带了很多实用程序类型,因此我们不必在每个项目中重写它们。

让我们看看一些最常见的:Omit、Partial、Readonly、Readonly、Exclude、Extract、NonNullable 和 ReturnType。

让我们看看其中的一个再行动。假设我们要将名为 Teacher 的实体的所有属性转换为只读。我们可以使用什么实用程序?

我们可以使用 Readonly 实用程序类型。让我们看看它的实际效果:

interface Teacher { name: string; email: string;}
type ReadonlyTeacher = Readonly<Teacher>;
const t: ReadonlyTeacher = { name: 'jose', email: '[email protected]'};
t.name = 'max'; // Error: Cannot assign to 'name' because it is a read-only property.(2540)

让我们回顾一下Readonly 在底层是如何工作的:

type Readonly<T> = { readonly [P in keyof T]: T[P]; }

现在让我们创建我们的自定义实用程序以获得乐趣。让我们反转 Readonly 类型以创建一个 Writable 类型:

interface Teacher { readonly name: string; readonly email: string;}
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
const t: Writeable<Teacher> = { name: 'jose', email: '[email protected]' };
t.name = 'max'; // works fine

注意:注意 - 修饰符。在这种情况下,它用于删除 readonly 修饰符。它可用于从属性中删除其他修饰符,例如 ?。

5、类型保护

类型保护是一组帮助我们缩小对象类型的工具。这意味着我们可以从更一般的类型转到更具体的类型。

有多种技术可以执行类型保护。在本文中,我们将只关注用户定义的类型保护。这些基本上是断言——就像任何给定类型的函数一样。

我们如何使用它们?我们只需要定义一个函数,它的返回类型是一个类型谓词,它返回true/false。让我们看看如何将 typeof 运算符转换为类型保护函数:

function isNumber(x: any): x is number { return typeof x === "number";}
function add1(value: string | number) { if (isNumber(value)) { return value +1; } return +value + 1;}

请注意,如果 isNumber 检查为 false,则 TypeScript 可以假定 value 将是一个字符串,因为 x 可能是字符串或数字。

让我们看另一个使用自定义接口的类型保护示例:

interface Hunter { hunt: () => void;}
// function type guardfunction isHunter(x: unknown): x is Hunter { return (x as Hunter).hunt !== undefined;}
const performAction = (x: unknown) => { if (isHunter(x)) { x.hunt(); }}
const animal = { hunt: () => console.log('hunt')}
performAction(animal);
注意 isHunter 函数的返回类型是 x is Hunter。该断言函数将成为我们的类型保护。
类型保护是有作用域的。在 isHunter(x) 代码块中,x 变量的类型为 Hunter。这意味着我们可以安全地调用它的hunt 方法。然而,在这个代码块之外,x 类型仍然是未知的。
最后的想法
在本文中,我们只是探讨了我们可以使用的最重要的 Typescript 功能。由于这只是一个概述,我们只是触及了它们的表面。
我的目标是让你好奇并展示 Typescript 的能力。现在由你来进一步深入研究其中任何一个。
通过尝试逐步采用它们,你将看到你的代码如何变得更整洁、更干净、更易于维护。
感谢你的阅读,祝编程愉快!

学习更多技能

你应该掌握的 5 大 TypeScript 功能

你应该掌握的 5 大 TypeScript 功能
web前端开发
一个专业而懂你的前端学习平台,分享你需要的编程开发与职场技能知识,我们坚持,每天学习一点点,每天进步一点点,人生进步一大步!关注我们,与我们一起学习进步,通过学习遇见更好的自己。
117篇原创内容
Official Account