TypeScript提示和技巧

本文深入探讨了TypeScript的高级特性,包括只读类型用于确保对象不变性,联合类型实现多种数据类型的组合,交集类型创建复合对象,以及如何利用泛型实现更灵活的函数签名。此外,还介绍了使用路径别名简化模块导入和选择支持TypeScript的库以提高代码质量。这些技巧对于提升TypeScript代码的健壮性和可维护性至关重要。
摘要由CSDN通过智能技术生成

目录

介绍

只读类型

联合类型

数字和字符串联合类型

限制可接受的类型

识别联合

交集类型

TypeScript泛型

使用TypeScript的路径别名

使用具有内置TypeScript支持的库

结论


介绍

TypeScriptJavaScript的超集。它类似于JavaScript,但具有超能力。

Vanilla JavaScript是一种动态类型语言。例如,如果您将数字类型分配给变量,然后在代码中进一步将字符串分配给相同的变量,那么JavaScript编译得很好。只有在生产中出现问题时才会出现错误。

如果您反对使用TypeScript之类的静态类型工具,则可以使用JavaScript linter提供一些类型检查。在我们的示例中,linter将有助于捕获错误。然而,Linter有其局限性。例如,linter不支持联合类型——我们将在本文中进一步探讨这一点——它们也不能lint复杂的对象结构。

TypeScriptJavaScript提供了更多类型功能,使您能够更好地构建代码库。它在编译时对您的代码库进行类型检查,并有助于防止可能使其投入生产的错误。

TypeScript以许多不同的方式改进了JavaScript开发。但在本文中,我们将重点关注六个领域,包括使用TypeScript的有用提示和技巧。其中一些技巧将讨论TypeScript支持函数式编程的方式。

只读类型

函数式编程通常需要不可变的变量——并且通过扩展,也需要不可变的对象。具有四个属性的对象在其整个生命周期中必须具有相同的属性,并且这些属性的值在任何时候都不能改变。

TypeScript通过Readonly实用程序类型使这成为可能。这是没有它的类型:

...  
type Product = {  
    name: string  
    price: number  
}

const products: Product[] = [  
{  
    name: "Apple Watch",  
    price: 400,  
},  
{  
    name: "Macbook",  
    price: 1000,  
},  
]

products.forEach(product => {  
    // mutating here  
    product.price = 500  
})  
...

在代码中,我们改变了price属性。由于新值是数字数据类型,TypeScript不会抛出错误。但是使用Readonly,我们的代码如下:

...  
const products: Readonly<Product>[] = [  
{  
    name: "Apple Watch",  
    price: 400,  
},  
{  
    name: "Macbook",  
    price: 1000,  
},  
]

products.forEach(product => {  
    // mutating here  
    product.price = 500  
})  
...

正如我们在此屏幕截图中看到的,该price属性是只读的,不能被分配另一个值。试图改变这个对象的值会抛出一个错误。

联合类型

TypeScript允许您以引人注目的方式组合类型。两种或多种类型的组合称为联合类型。您使用"|" 符号来创建联合类型。让我们看一些例子。

数字和字符串联合类型

有时,您需要一个变量作为数字。其他时候,您需要相同的变量才能成为string

使用TypeScript,您可以通过执行以下简单操作来实现此目的:

function(id: number | string) {  
    ...  
}

此处声明的联合类型的挑战是您无法调用id.toUpperCase,因为TypeScript不知道您是要在函数声明期间传递字符串还是数字。因此,要使用该toUpperCase方法,您必须使用typeof === "string"检查id是否是string

但是,如果它不能对所有组成特定联合的成员应用标准方法,TypeScript不会抱怨。

限制可接受的类型

使用联合,您还可以限制变量可接受的数据类型值。您可以使用文字类型来执行此操作。这是一个例子:

function(type: "picture" | "video") {  
    ...  
}

该联合类型包括图片和视频的字符串文字类型。此代码会导致其他字符串值引发错误。

识别联合

联合的另一个好处是,您可以拥有不同结构的对象类型,每个对象类型都有一个共同的区别属性。这是一个例子:

...  
type AppleFruit = {  
    color: string;  
    size: "small" | "large"  
}

type OrangeFruit = {  
    isRipe: boolean;  
    count: number;  
}

function describeFruit(fruit: AppleFruit | OrangeFruit) {  
...  
}  
...

在这段代码中,我们有一个联合类型Fruit,它由两种不同的对象类型组成:AppleFruitOrangeFruit。这两种水果没有共同的特性。这种差异使得TypeScript在我们使用的时候很难知道水果是什么,如下面的代码和截图所示:

...  
function describeFruit(fruit: AppleFruit | OrangeFruit) {  
    if (fruit.color) {  
        // throw error...see Figure B.  
    }  
}  
...

此屏幕截图中的错误表明橙色类型上的color不存在。有两种解决方案。

第一个解决方案是以更可接受的方式检查该color属性是否存在。就是这样:

...  
function describeFruit(fruit: AppleFruit | OrangeFruit) {  
    if ("color" in fruit) {  
        // now typescript knows fruit is of the apple type  
    }  
}  
...

我们检查color属性是否在fruit对象中。使用此检查,TypeScript可以正确推断类型,如下图所示:

第二种解决方案是使用有区别的联合。这种方法意味着具有清楚地区分两个对象的属性。TypeScript可以使用该属性来了解在特定时间正在使用哪种类型。就是这样:

...  
type AppleFruit = {  
    name: "apple";  
    color: string;  
    size: "small" | "large";  
}

type OrangeFruit = {  
    name: "orange";  
    isRipe: boolean;  
    count: number;  
}

function describeFruit(fruit: AppleFruit | OrangeFruit) {  
    if (fruit.name === "apple") {  
        // apple type detected  
    }  
}  
...

由于这两种类型都有name属性,fruit.name因此不会抛出错误。并且,使用name属性的值,TypeScript可以确定fruit类型。

交集类型

与涉及type1type2, __type3的联合类型相比,交集类型是type1,type2  type3

这些类型之间的另一个显著区别是,虽然联合类型可以是字符串或数字,但交集类型不能是字符串和数字。数据不能同时是字符串和数字。因此,交集类型涉及对象。

既然我们已经讨论了联合和交集类型之间的区别,让我们来探索一些做交集的方法:

...  
interface Profile {  
    name: string;  
    phone: string;  
}

interface AuthCreds {  
    email: string;  
    password: string;  
}

interface User: Profile & AuthCreds  
...

ProfileAuthCreds是相互独立存在的接口类型的示例。这种独立性意味着您可以创建一个Profile类型的对象和另一个AuthCreds类型的对象,并且这些对象可能不会关联在一起。但是,您可以将这两种类型相交以制作更大的类型:User。此类型的结构是一个具有四个属性的对象:nameemail passwordphone,所有字符串类型。

现在你可以像这样创建一个User对象:

...  
const user:User = {  
    name: "user";  
    phone: "222222",  
    email: "user@user.com"  
    password: "***"  
}  
...

TypeScript泛型

有时,当您创建一个函数时,您知道它的返回类型。这是一个例子:

...  
interface AppleFruit {  
    size: number  
}

interface FruitDescription {  
    description: string;  
}

function describeFruit(fruit: AppleFruit): AppleFruit & FruitDescription {  
    return {  
        ...fruit,  
        description: "A fruit",  
    }  
}

const fruit: AppleFruit = {  
    size: 50  
}

describeFruit(fruit)  
...

在此示例中,该describeFruit函数接受AppleFruit类型的fruit参数。它返回由AppleFruitFruitDescription类型组成的交集类型。

但是,如果您希望此函数返回不同水果类型的描述,该怎么办?泛型在这里是相关的。这是一个例子:

...  
interface AppleFruit {  
    size: number  
}

interface OrangeFruit {  
    isOrangeColor: boolean;  
}

interface FruitDescription {  
    description: string;  
}

function describeFruit<T>(fruit: T): T & FruitDescription {  
    return {  
        ...fruit,  
        description: "A fruit",  
    }  
}

const appleFruit: AppleFruit = {  
    size: 50  
}

describeFruit(appleFruit)

const orangeFruit: OrangeFruit = {  
    isOrangeColor: true  
}

describeFruit<OrangeFruit>(orangeFruit)  
...

泛型函数describeFruit接受不同的类型。代码在调用函数时确定要传递的水果类型。

我们第一次调用describeFruit时,TypeScript会自动推断TAppleFruit因为appleFruit是属于那种类型。

下一次,我们在调用函数之前要使用OrangeFruit指定TOrangeFruit类型。

这些行做同样的事情,但在某些情况下,自动推理可能不准确。

在我们的示例中,我们可以将不同的类型传递给该函数,它只是返回我们传递的类型的FruitDescription交集。

这是使用泛型将类型传递给函数的示例:

describeFruit函数有一个类型,正如我们最初用使用OrangeFruit定义的那样。

使用TypeScript的路径别名

你可能通常import,如下所示:

...  
import Button from "../../../../components/Button"  
...

此命令import可能位于需要此组件的不同文件中。当您更改Button文件的位置时,您还需要在使用它的各个文件中更改此import行。此调整还会导致在版本控制中跟踪更多文件更改。

我们可以通过使用别名路径来改进我们的导入方式。

TypeScript使用tsconfig.json文件来存储配置,使其能够按照您的需要工作。在里面,有paths属性。此属性允许您为应用程序中的不同目录设置路径别名。下面是它的样子,使用compilerOptionsbaseUrlpaths

...  
{  
    "compilerOptions": {  
        "baseUrl": ".", // required if "paths" is specified.  
        "paths": {  
            "components/*": ["./src/components/*"] // path is relative to the baseUrl  
        }  
    }  
}  
...

使用组件别名,您现在可以像这样导入:

...  
import Button from "components/Button"  
...

无论您在目录中有多深,使用此命令都能正确解析Button文件。

现在,当您更改组件的位置时,您所要做的就是更新paths属性。这种方法意味着更一致的文件和更少的版本控制文件更改。

使用具有内置TypeScript支持的库

当您使用不支持TypeScript的库时,您会错过TypeScript的好处。即使是一个简单的错误——例如对参数或对象属性使用错误的数据类型——也可能在没有TypeScript提供的警告的情况下让你头疼。如果没有此警报,您的应用程序可能会崩溃,因为它需要一个字符串,但您传递了一个数字。

并非每个库都支持TypeScript。当你安装一个支持TypeScript的库时,它会安装带有TypeScript声明文件的分布式代码。

结论

在本文中,我们探讨了TypeScript如何改进JavaScript编码。我们还讨论了TypeScript如何支持一些函数式编程技术。这种能力使TypeScript适合面向对象编程(OOP)开发人员和函数式程序员。

下一步,您可以深入了解TypeScript配置选项TypeScript提供了这个详尽的资源来帮助您构建下一个应用程序。

https://www.codeproject.com/Articles/5322795/TypeScript-Tips-and-Tricks

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值