当前位置:首页 > 科技  > 软件

解锁TypeScript的潜力:改进标准库类型

来源: 责编: 时间:2023-08-20 23:17:07 773观看
导读在 TypeScript 项目中,我们的编写代码并不是唯一的代码。标准库和运行环境也会参与类型检查。这些包括在全局范围内可用的JavaScript方法和Web平台API,包括用于处理数组、window对象、Fetch API等方法。本文将探讨TypeS

Xiy28资讯网——每日最新资讯28at.com

在 TypeScript 项目中,我们的编写代码并不是唯一的代码。标准库和运行环境也会参与类型检查。这些包括在全局范围内可用的JavaScript方法和Web平台API,包括用于处理数组、window对象、Fetch API等方法。本文将探讨TypeScript标准库最常见的问题以及编写更安全、可靠的代码的方法!Xiy28资讯网——每日最新资讯28at.com

1、TypeScript 标准库的问题

Summer IS HERE

TypeScript的标准库在很大程度上提供了高质量的类型定义,但是一些广泛使用的 API 的类型声明可能过于宽松或过于严格Xiy28资讯网——每日最新资讯28at.com

过于宽松类型的最常见问题是使用 any 而不是更精确的类型,比如 unknown。在标准库中,Fetch API 是导致类型安全问题最常见的来源。json()方法返回的是 any 类型的值,这可能导致运行时错误和类型不匹配,同样的情况也适用于JSON.parse方法。Xiy28资讯网——每日最新资讯28at.com

async function fetchPokemons() {  const response = await fetch('https://pokeapi.co/api/v2/pokemon');  const data = await response.json();  return data;}const pokemons = await fetchPokemons();//    ^?  anypokemons.data.map(pokemon => pokemon.name);//            ^  TypeError: Cannot read properties of undefined

另一方面,有一些API的类型声明过于限制,可能导致开发体验较差。例如,Array.filter方法的工作方式与直觉相反,需要手动进行类型转换或编写类型保护。Xiy28资讯网——每日最新资讯28at.com

// filteredArray 的类型是 Array<number | undefined>。const filteredArray = [1, 2, undefined].filter(Boolean);// filteredArray的类型是 Array<number>const filteredArray = [1, 2, undefined].filter(  (item): item is number => Boolean(item));

没有简单的方法来升级或替换标准库的类型声明,因为它的类型定义是随 TypeScript 编译器一起提供的。然而,如果想充分利用 TypeScript,有多种方法可以解决这个问题。Xiy28资讯网——每日最新资讯28at.com

下面来以 Fetch API 为例探讨一些选项。Xiy28资讯网——每日最新资讯28at.com

2、使用类型断言

Summer IS HERE

一个快速解决方案是手动指定类型。为了做到这一点,需要描述响应的格式并将any类型转换为所需的类型。这样就可以将对any的使用限制在代码库的一小部分中,比在整个程序中都使用返回的any类型要好很多。Xiy28资讯网——每日最新资讯28at.com

interface PokemonListResponse {  count: number;  next: string | null;  previous: string | null;  results: Pokemon[];}interface Pokemon {  name: string;  url: string;}async function fetchPokemons() {  const response = await fetch('https://pokeapi.co/api/v2/pokemon');  const data = await response.json() as PokemonListResponse;  return data;}const pokemons = await fetchPokemons();//    ^?  PokemonListResponse

另外,TypeScript 现在将会突出显示对不存在字段的访问的错误。然而,类型转换给我们带来了额外的责任,即准确描述从服务端返回的类型。Xiy28资讯网——每日最新资讯28at.com

pokemons.data.map(pokemon => pokemon.name);//       ^  Error: “Pokemon List Response”类型中不存在属性“data”//          应该在这里使用 'results' 字段

类型断言可能存在风险,应谨慎使用。如果断言不正确,可能会导致意外行为。例如,在描述类型时容易犯错,比如忽略字段可能为null或undefined的情况。Xiy28资讯网——每日最新资讯28at.com

3、使用类型保护

Summer IS HERE

我们可以通过首先将any强制转换为unknown来增强上述解决方案。这清楚地表明 fetch 函数可以返回任何类型的数据。然后需要通过编写类型保护来验证响应是否具有需要的数据,如下所示:Xiy28资讯网——每日最新资讯28at.com

function isPokemonListResponse(data: unknown): data is PokemonListResponse {    if (typeof data !== 'object' || data === null) return false;    if (typeof data.count !== 'number') return false;    if (data.next !== null && typeof data.next !== 'string') return false;    if (data.previous !== null && typeof data.previous !== 'string') return false;    if (!Array.isArray(data.results)) return false;    for (const pokemon of data.results) {        if (typeof pokemon.name !== 'string') return false;        if (typeof pokemon.url !== 'string') return false;    }    return true;}

类型守卫函数接收一个具有unknown类型的变量作为输入。使用is操作符来指定输出类型,表示已经检查了data变量中的数据,并且它具有这种类型。在函数内部,编写所有必要的检查,以验证需要的所有字段。Xiy28资讯网——每日最新资讯28at.com

可以使用得到的类型守卫来将unknown类型缩小到想要处理的类型。这样,如果响应数据格式发生变化,可以迅速检测到并在应用逻辑中处理该情况。Xiy28资讯网——每日最新资讯28at.com

async function fetchPokemons() {    const response = await fetch('https://pokeapi.co/api/v2/pokemon');    const data = (await response.json()) as unknown;    if (!isPokemonListResponse(data)) {        throw new Error('error');    }    return data;}const pokemons = await fetchPokemons();//    ^?  PokemonListResponse

然而,编写类型守卫可能会很繁琐,特别是在处理大量数据时。此外,在类型守卫中犯错的风险很高,这等同于在类型定义本身上犯错。Xiy28资讯网——每日最新资讯28at.com

4、使用 Zod 库

Summer IS HERE

为了简化类型保护的编写,可以使用 Zod 等数据验证库。使用 Zod,可以定义一个数据模式,然后调用一个函数来根据该模式检查数据格式。Xiy28资讯网——每日最新资讯28at.com

import { z } from 'zod';const schema = z.object({    count: z.number(),    next: z.string().nullable(),    previous: z.string().nullable(),    results: z.array(        z.object({            name: z.string(),            url: z.string(),        })    ),});

这类库最初是以TypeScript为目标开发的。它们允许我们只需一次描述数据模式,然后自动生成类型定义。这消除了手动编写TypeScript接口的需要,并避免了重复工作。Xiy28资讯网——每日最新资讯28at.com

type PokemonListResponse = z.infer<typeof schema>;

这个函数本质上就像一个类型守卫,而我们无需手动编写它。Xiy28资讯网——每日最新资讯28at.com

async function fetchPokemons() {    const response = await fetch('https://pokeapi.co/api/v2/pokemon');    const data = (await response.json()) as unknown;    return schema.parse(data);}const pokemons = await fetchPokemons();//    ^?  PokemonListResponse

因此,我们得到了一种可靠的解决方案,不留下任何犯错的机会。由于不再手动编写类型定义,因此无法出现类型定义的错误。类型守卫也不会出现错误。模式中可能会出现错误,但可以在开发过程中察觉到这些错误。Xiy28资讯网——每日最新资讯28at.com

5、Zod 的替代品

Summer IS HERE

Zod有许多不同的替代品,它们在功能、捆绑大小和性能上有所区别。对于每个应用程序,您可以选择最合适的选项。Xiy28资讯网——每日最新资讯28at.com

例如,superstruct 库是Zod的一个更轻量级的替代方案。由于该库相对较小(13.1 kB 对比 3.4 kB),因此更适合在客户端使用。Xiy28资讯网——每日最新资讯28at.com

typia 库是一种稍微不同的方法,它采用提前编译的方式。由于编译阶段,数据验证的速度更快。这对于大量数据或服务端重型代码特别重要。Xiy28资讯网——每日最新资讯28at.com

6、解决根本问题

Summer IS HERE

使用 Zod 等库进行数据验证可以帮助克服 TypeScript 标准库中任何类型的问题。但是,了解返回 any 的标准库方法仍然很重要,并在使用这些方法时将这些类型替换为unknown。Xiy28资讯网——每日最新资讯28at.com

理想情况下,标准库应该使用unknown类型而不是any。这将使编译器能够建议所有需要类型保护的地方。幸运的是,TypeScript 的声明合并功能提供了这种可能性。Xiy28资讯网——每日最新资讯28at.com

在 TypeScript 中,接口有一个有用的功能,即同名接口的多个声明将合并为一个声明。例如,如果有一个带有name字段的 User 接口,然后声明另一个带有age字段的 User 接口,则生成的 User 接口将同时具有name和age字段。Xiy28资讯网——每日最新资讯28at.com

interface User {    name: string;}interface User {    age: number;}const user: User = {    name: 'CUGGZ',    age: 25,};

这个特性不仅在单个文件内有效,而是在整个项目范围内全局有效。这意味着可以使用这个特性来扩展 Window 类型,甚至是扩展外部库的类型,包括标准库。Xiy28资讯网——每日最新资讯28at.com

declare global {    interface Window {        sayHello: () => void;    }}window.sayHello();//     ^  TypeScript 现在知道这个方法了

通过使用声明合并,可以完全解决TypeScript标准库中 any 类型的问题。Xiy28资讯网——每日最新资讯28at.com

7、Fetch API 更好的类型

Summer IS HERE

为了改进标准库中的 Fetch API,需要更正 json() 方法的类型,以便它始终返回 unknown 而不是any。首先,确定json方法是 Response 接口的一部分。Xiy28资讯网——每日最新资讯28at.com

interface Response extends Body {    readonly headers: Headers;    readonly ok: boolean;    readonly redirected: boolean;    readonly status: number;    readonly statusText: string;    readonly type: ResponseType;    readonly url: string;    clone(): Response;}

在Response的方法中找不到json()方法。相反,可以看到 Response 接口继承自 Body 接口。因此,查看 Body 接口以找到需要的方法。可以看到,json()方法实际上返回any类型。Xiy28资讯网——每日最新资讯28at.com

interface Body {    readonly body: ReadableStream<Uint8Array> | null;    readonly bodyUsed: boolean;    arrayBuffer(): Promise<ArrayBuffer>;    blob(): Promise<Blob>;    formData(): Promise<FormData>;    text(): Promise<string>;    json(): Promise<any>;}

为了解决这个问题,可以在项目中再定义一次 Body 接口,如下所示:Xiy28资讯网——每日最新资讯28at.com

declare global {    interface Body {        json(): Promise<unknown>;    }}

由于声明合并, json()方法现在将始终返回 unknown 类型。Xiy28资讯网——每日最新资讯28at.com

async function fetchPokemons() {    const response = await fetch('https://pokeapi.co/api/v2/pokemon');    const data = await response.json();    //    ^?  unknown    return data;}

8、JSON.parse 更好的类型

Summer IS HERE

同样,我们可以修复JSON解析的问题。默认情况下,parse()方法返回any类型,这可能会在使用解析后的数据时导致运行时错误。Xiy28资讯网——每日最新资讯28at.com

const data = JSON.parse(text);//    ^?  any

为了解决这个问题,我们需要知道 parse() 方法是 JSON 接口的一部分。然后可以在项目中声明类型,如下所示:Xiy28资讯网——每日最新资讯28at.com

declare global {    interface JSON {        parse(            text: string,             reviver?: (this: any, key: string, value: any) => any        ): unknown;    }}

现在,JSON 解析总是返回unknow类型,这可以带来更安全、更易于维护的代码库。Xiy28资讯网——每日最新资讯28at.com

const data = JSON.parse(text);//    ^?  unknown

9、Array.isArray 更好的类型

Summer IS HERE

另一个常见的例子是检查变量是否是数组。默认情况下,此方法返回包含any类型的数组,基本上与使用any没有区别。Xiy28资讯网——每日最新资讯28at.com

if (Array.isArray(userInput)) {    console.log(userInput);    //          ^?  any[]}

这里可以扩展数组构造函数的类型,如下所示,该方法现在返回一个包含unknown类型的数组,这样更安全和准确。Xiy28资讯网——每日最新资讯28at.com

declare global {    interface ArrayConstructor {        isArray(arg: any): arg is unknown[];    }}if (Array.isArray(userInput)) {    console.log(userInput);    //          ^?  unknown[]}

10、structuredClone 更好的类型

Summer IS HERE

最近引入的克隆对象方法 structuredClone 也返回 any。Xiy28资讯网——每日最新资讯28at.com

const user = {    name: 'John',    age: 30,};const copy = structuredClone(user);//    ^?  any

修复这个问题和前面的方法一样。不过,在这种情况下,需要添加一个新的函数签名而不是扩展接口。幸运的是,声明合并对函数也同样适用。因此,可以按照以下方式修复这个问题:Xiy28资讯网——每日最新资讯28at.com

declare global {    declare function structuredClone<T>(value: T, options?: StructuredSerializeOptions): T;}

现在克隆的对象将具有与原始对象具有相同的类型。Xiy28资讯网——每日最新资讯28at.com

const user = {    name: 'John',    age: 30,};const copy = structuredClone(user);//    ^?  { name: string, age: number }

11、Array.filter 更好的类型

Summer IS HERE

声明合并不仅对修复any类型的问题很有用,还可以改进标准库的易用性。下面来看一下Array.filter方法的例子。Xiy28资讯网——每日最新资讯28at.com

const filteredArray = [1, 2, undefined].filter(Boolean);//    ^?  Array<number | undefined>

可以让 TypeScript 在应用 Boolean 过滤函数后自动缩小数组类型。为此,需要扩展 Array 接口,如下所示:Xiy28资讯网——每日最新资讯28at.com

type NonFalsy<T> = T extends false | 0 | "" | null | undefined | 0n ? never : T;declare global {    interface Array<T> {      filter(predicate: BooleanConstructor, thisArg?: any): Array<NonFalsy<T>>;    }}

现在可以使用filter的简写形式,并获得正确的数据类型作为结果。Xiy28资讯网——每日最新资讯28at.com

12、ts-reset

Summer IS HERE

TypeScript 的标准库包含超过 1000 个 any 类型的实例。在使用严格类型的代码时,有很多地方可以改善开发体验。避免手动修复标准库的一种解决方案是使用 ts-reset 库。它易于使用,只需在项目中导入一次。Xiy28资讯网——每日最新资讯28at.com

import "@total-typescript/ts-reset";

该库相对较新,因此它对标准库的修复还没有想要的那么多。然而,我相信这只是一个开始。值得注意的是,ts-reset 仅包含对全局类型的安全更改,不会导致潜在的运行时错误。Xiy28资讯网——每日最新资讯28at.com

13、避免在库中使用

Summer IS HERE

改进 TypeScript 的标准库有很多好处。然而,值得注意的是,重新定义标准库的全局类型限制了这种方法仅适用于应用。不适用于库,因为使用这样的库会意外地改变应用的全局类型的行为。Xiy28资讯网——每日最新资讯28at.com

一般来说,建议避免在库中修改 TypeScript 的标准库类型。相反,可以使用静态分析工具在代码质量和类型安全方面获得类似的结果,这适用于库开发。Xiy28资讯网——每日最新资讯28at.com

14、总结

Summer IS HERE

TypeScript的标准库是TypeScript编译器的一个关键组成部分,提供了一系列内置类型,用于处理JavaScript和Web平台API。然而,标准库并不完美,其中一些类型声明存在问题,可能导致代码中的类型检查质量较低。本文探讨了一些TypeScript标准库常见的问题以及编写更安全、更可靠的代码的方法。Xiy28资讯网——每日最新资讯28at.com

通过使用类型断言、类型守卫和类似Zod的库,可以改善代码库的类型安全性和代码质量。此外,还可以通过使用声明合并来修复问题的根本,从而改善TypeScript标准库的类型安全性和人性化程度。Xiy28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-6177-0.html解锁TypeScript的潜力:改进标准库类型

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 三言两语说透设计模式的艺术-适配器模式

下一篇: 最新技术趋势:RabbitMQ在云原生应用中的应用

标签:
  • 热门焦点
  • 小米官宣:2023年上半年出货量中国第一!

    今日早间,小米电视官方微博带来消息,称2023年小米电视上半年出货量达到了中国第一,同时还表示小米电视的巨屏风暴即将开始。“公布一个好消息2023年#小米电视上半年出货量中国
  • Redmi Pad评测:红米充满野心的一次尝试

    从Note系列到K系列,从蓝牙耳机到笔记本电脑,红米不知不觉之间也已经形成了自己颇有竞争力的产品体系,在中端和次旗舰市场上甚至要比小米新机的表现来得更好,正所谓“大丈夫生居
  • Golang 中的 io 包详解:组合接口

    io.ReadWriter// ReadWriter is the interface that groups the basic Read and Write methods.type ReadWriter interface { Reader Writer}是对Reader和Writer接口的组合,
  • 如何正确使用:Has和:Nth-Last-Child

    我们可以用CSS检查,以了解一组元素的数量是否小于或等于一个数字。例如,一个拥有三个或更多子项的grid。你可能会想,为什么需要这样做呢?在某些情况下,一个组件或一个布局可能会
  • 零售大模型“干中学”,攀爬数字化珠峰

    文/侯煜编辑/cc来源/华尔街科技眼对于绝大多数登山爱好者而言,攀爬珠穆朗玛峰可谓终极目标。攀登珠峰的商业路线有两条,一是尼泊尔境内的南坡路线,一是中国境内的北坡路线。相
  • 中国家电海外掘金正当时|出海专题

    作者|吴南南编辑|胡展嘉运营|陈佳慧出品|零态LT(ID:LingTai_LT)2023年,出海市场战况空前,中国创业者在海外纷纷摩拳擦掌,以期能够把中国的商业模式、创业理念、战略打法输出海外,他们依
  • AI芯片初创公司Tenstorrent获三星和现代1亿美元投资

    Tenstorrent是一家由芯片行业资深人士Jim Keller领导的加拿大初创公司,专注于开发人工智能芯片,该公司周三表示,已经从现代汽车集团和三星投资基金等
  • 机构称Q2全球智能手机出货量同比下滑11% 苹果份额依旧第2

    7月20日消息,据外媒报道,研究机构的报告显示,由于需求下滑,今年二季度全球智能手机的出货量,同比下滑了11%,三星、苹果等主要厂商的销量,较去年同期均有下
  • 英特尔Xe HPG游戏显卡:拥有512EU,单风扇版本

    据10 月 30 日外媒 TheVerge 消息报道,英特尔 Xe HPG Arc Alchemist 的正面实被曝光,不仅拥有 512 EU 版显卡,还拥有 128EU 的单风扇版本。另外,这款显卡 PCB
Top