您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > javascript

关于TypeScript中的泛型,希望这篇文章能让你彻底理解泛型

时间:2024-04-23 11:12:13  来源:今日头条  作者:
在我们今天的旅程中,我们一起探索TypeScript中那些令人兴奋的泛型知识。从类型推断的便捷性到泛型在日常编程中的灵活运用,希望这些内容能够帮助你解开围绕泛型的所有迷雾。记住,泛型不仅仅是类型安全的保障,它还能让你的代码更加简洁、更易于维护。

在编程世界里,我们经常会遇到一个情况:阅读那些充满了虚构示例的枯燥文档,实在是让人提不起兴趣。因此,在这篇文章中,我想和大家分享一些我在实际开发过程中遇到的泛型(Generics)使用案例。通过这些真实的例子,相信泛型的概念对你来说会更加具有意义,也更容易理解。

泛型简介

那么,泛型究竟是什么呢?简而言之,泛型允许我们编写能够适用于广泛的原始类型和对象的类型安全代码。在声明新类型、接口、函数和类时,都可以使用泛型。这听起来可能有点抽象,那么让我们直接进入正题,看看泛型的一些实际用例吧。

代码重复

有时候,在我们开发的时候会遇到一些重复性的工作,特别是当我们要处理不同类型的数据时。这里有个很好的例子,就是我们的服务器需要返回用户和书籍信息。通常情况下,如果没有泛型(Generics),我们可能需要为每种资源分别定义一个响应类型。

举个例子,你的服务器需要返回用户信息和书籍信息。

如果没有泛型,你可能需要为用户和书籍分别定义两个相似的响应类型,就像下面这样:

// 用户信息类型
type User = { name: string };
type UsersResponse = {
  data: User[];
  total: number;
  page: number;
  limit: number;
};

// 书籍信息类型
type Book = { isbn: string };
type BooksResponse = {
  data: Book[];
  total: number;
  page: number;
  limit: number;
};

但这种方式会产生很多重复的代码,不利于后期维护。而泛型,它的妙处就在于可以让我们定义一个通用的响应形状,然后再根据需要使用不同的数据类型来复用这个形状,这样就能减少重复的代码,看看下面这个改进版:

// 分页响应的泛型定义
type PaginatedResponse<T> = {
  data: T[];
  total: number;
  page: number;
  limit: number;
};

// 使用泛型定义用户和书籍的响应类型
type UsersResponse = PaginatedResponse<User>;
type BooksResponse = PaginatedResponse<Book>;

使用了泛型之后,无论是处理用户列表还是书籍列表,我们只需要写一次响应结构,就可以应用到各种不同的数据类型上了,不是很方便吗?

泛型就像是一个万能的模具,你只需要根据不同的需求,换上不同的"原料",它就能帮你塑形出符合要求的"产品"。这样我们的代码就会变得更简洁、更有可读性,也更容易维护。

现在来想想,你是否能在你的项目中找到那些可以用泛型来简化的地方呢?别小看这个小改变,它可能会为你省下不少时间和精力哦!

泛型,让函数的逻辑和类型更匹配

在软件开发中,我们常常需要编写一些根据特定属性筛选数组元素的函数。比如我们有一个筛选数组的函数 filterArrayByValue,它可以基于我们提供的属性和值来过滤数组。函数的参数和返回值之间的关系非常紧密。

一开始,我们的函数可能看起来是这样的:

function filterArrayByValue(items, propertyName, valueToFilter) {
  return items.filter((item) => item[propertyName] === valueToFilter);
}

这个函数声明说,它接受一个项目数组,并返回一个具有相同类型项目的数组。目前为止,一切都好。

但是这里有个问题,我们的 propertyName 参数被定义为字符串类型,这看似没问题,但它可能会导致我们不小心传入了不存在于类型 T 的项的属性名。如果我们定义了一个用户数组,它应该是这样的:

type User = { name: string; age: number };
const users: User[] = [
  { name: 'Vasya', age: 32 },
  { name: 'Anna', age: 12 },
];

现在,如果我们尝试传递一个错误的属性,在这种情况下它不会破坏应用程序,只是返回一个空数组,但是这并不是我们希望的,我们希望编译器会提示属性不匹配的问题。

filterArrayByValue(users, 'notExistField', 'Vasya');

让我们定义该函数的第二个参数,它将描述限定为只能为T类型的相关的属性

我们定义完后,发现在运行阶段之前提示传递了错误的属性。

接下来我们使用 number 类型的age 属性。正如您可能预测的那样,当我们尝试按此字段过滤项目时,我们会遇到问题:

filterArrayByValue(users, 'age', 12);

接下来我们修改过滤函数,valueToFilter参数的对应关系,匹配为T类型属性对应的值

修改后,问题已经消失了,现在我们无法将除了数字以外的其他类型的值作为年龄属性值传递,因为用户类型只允许该属性为数字,这正是我们需要的。

在 React 中的应用

在React开发中,状态管理是一个核心概念,尤其是在使用函数组件和Hooks的时候。给出的代码段展示了如何在React组件中使用 useState Hook来管理一个用户对象的状态,并提供了一个 setUserField 函数来更新用户对象的特定字段。原始版本的函数对于字段名和字段值使用了非常宽泛的类型定义,这可能会导致类型安全问题。

const setUserField = (field: string, value: any) => 
  setUser(prevUser => ({...prevUser, [field]: value}));

这里,field 是任意的字符串,而 value 是任意类型,这意味着我们可以不小心将错误的数据类型赋值给用户对象的属性,TypeScript编译器也不会提出警告。

为了提高类型安全性,可以使用泛型来约束 field 必须是 User 类型的键,value 必须是对应于该键的 User 类型的值。改进后的 setUserField 函数如下:

function setUserField<KEY extends keyof User>(
    field: KEY, 
    value: User[KEY]
  ) {
    setUser((prevUser) => ({ ...prevUser, [field]: value }));
}

在这个改进的版本中,setUserField 函数现在接受两个参数:

  1. field:一个类型参数 KEY,它被限制为 User 类型的键的集合中的一个。
  2. value:一个 User[KEY] 类型的值,确保了传递给 setUserField 的值必须与 User 类型中 field 字段的类型相匹配。

这样一来,如果你尝试传递一个不正确的字段或者错误类型的值给 setUserField 函数,TypeScript编译器会提供类型错误的提示,从而减少运行时错误的可能性。这种模式特别有用,因为它可以保证我们对状态的更新是类型安全的,同时也保持了函数的灵活性。这是React中使用TypeScript的一个典型例子,展示了如何通过类型系统来增强代码质量。

同时保持灵活和严格(关键词“扩展extend”与泛型)

当我们在设计高阶组件(HOC)时,尤其是在React或React Native的环境下,我们希望这些HOC只能应用于具有某些属性的组件。在这个例子中,我们想要一个HOC,它仅适用于具有 style 属性的组件。

function withStyledComponent<StyleProp, Props extends { style?: StyleProp }>(
  Component: ComponentType<Props>
) {
  return (props: Props) => {
    const { style } = props;
    // 实现细节在此省略
    return <Component {...props} />;
  };
}

泛型的 extend 关键字允许我们定义一个类型 T,它必须至少具有类型 K 的所有属性。这样,我们就可以确保我们的HOC只会被用在正确的组件上。

在上述的 withStyledComponent HOC中,我们指定了任何使用此HOC的组件都必须有一个 style 属性。如果我们尝试将这个HOC应用于没有 style 属性的组件,TypeScript会抛出一个错误。

这种模式非常有用,因为它可以保证我们的HOC在类型安全的同时,也不限制组件的其他属性。这就意味着,尽管我们对 style 属性有明确的期望,但我们的组件可以自由地具有其他任何属性。

此外,由于TypeScript知道我们可能会在具有 style 属性的组件中使用我们的HOC,我们可以安全地从组件的属性中提取 style 并在HOC内部操作它。

TypeScript中的类型推断

TypeScript有一个令人惊叹的特性——它会尝试从上下文中推断出类型,只要有可能。比如,在代码中看到这样的语句时:

const a: number = 12;

这意味着开发者可能并不知道TypeScript已经知道a是一个从值推断出来的数字类型。

现在,假设我们用泛型定义了这样一个函数:

function identifyType<T>(target: T) {
  console.log("Type of target is", typeof target);
}

如果你是初学者,你可能会这样使用它:

identifyType<number>(5);

但是,TypeScript可以从你作为第一个参数传递的值中推断出泛型的类型,最好是这样使用:

identifyType(5);

如果你是React开发者,你可能会经常看到像这样的代码片段:

const [count, setCount] = useState<number>(5);

但同样,这里明确定义泛型类型是多余的,因为它会从你作为第一个参数传递的值中被推断出来。如果你是一位经验丰富的开发者,你的代码将看起来像这样:

const [count, setCount] = useState(5);

还有我遇到过的一个情况,有开发者害怕在React组件的props中使用泛型。是的,我们在JSX中使用我们的组件,他们不知道这样的语法是有效的:

function Component() {
  const data: ItemType[] = [{ value: '1' }];
  return (
    <RenderList<ItemType>
      data={data}
      renderItem={({ item }) => <Text>{item.value}</Text>}
    />
  );
}

是的,它看起来有些奇怪,但这里我们可以依靠TypeScript的能力,根据我们传递给组件的props类型来推断泛型类型:

<RenderList
  data={data}
  renderItem={({ item }) => <Text>{item.value}</Text>}
/>

认同这样的代码看起来更简洁,你看起来也像一个经验丰富的开发者。这就是TypeScript和泛型的魅力:它们提供了一种强大的类型系统,不仅可以帮助我们减少错误,还可以使代码更加简洁易读。通过这些例子,我们可以看到,TypeScript的类型推断功能可以在不牺牲类型安全的情况下,极大地简化代码。而泛型的灵活使用,则让我们的代码既严谨又富有弹性。

结束

在我们今天的旅程中,我们一起探索了TypeScript中那些令人兴奋的泛型知识。从类型推断的便捷性到泛型在日常编程中的灵活运用,希望这些内容能够帮助你解开围绕泛型的所有迷雾。记住,泛型不仅仅是类型安全的保障,它还能让你的代码更加简洁、更易于维护。

正如我们所见,合理利用TypeScript的类型推断,可以让我们避免冗余的代码,让逻辑表达更为直观。泛型的使用更是让组件和函数的复用性达到了新的高度。所以,当你下次遇到需要类型化处理多样化数据的场景时,别忘了,泛型就是你的得力助手。



Tags:TypeScript   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
关于TypeScript中的泛型,希望这篇文章能让你彻底理解泛型
在我们今天的旅程中,我们一起探索TypeScript中那些令人兴奋的泛型知识。从类型推断的便捷性到泛型在日常编程中的灵活运用,希望这些内容能够帮助你解开围绕泛型的所有迷雾。记...【详细内容】
2024-04-23  Search: TypeScript  点击:(0)  评论:(0)  加入收藏
一篇文章搞懂TypeScript
TypeScript 是 JavaScript 的超集,一方面给动态类型的 js 增加了类型校验,另一方面扩展了 js 的各种功能。原始数据类型 字符串 数值 布尔 null undefined Symbol BigIntlet s...【详细内容】
2024-01-08  Search: TypeScript  点击:(98)  评论:(0)  加入收藏
TypeScript中的null和undefined的区别
在TypeScript中,null和undefined是两个特殊的值,用于表示变量的缺失或未定义。尽管它们在某些情况下可能看起来相似,并且都可以表示"没有值",但它们在语义和用法上存在一些重要...【详细内容】
2023-12-07  Search: TypeScript  点击:(140)  评论:(0)  加入收藏
为什么要在项目中使用TypeScript?
译者 | 李睿随着越来越多的开发人员采用TypeScript,人们需要了解在下一个项目中应该使用TypeScript的原因。尽管它在早期应用中遇到了一些阻力,但在过去十年,它迅速成为一种广...【详细内容】
2023-11-30  Search: TypeScript  点击:(174)  评论:(0)  加入收藏
一文读懂 TypeScript 泛型及应用
泛型是静态类型语言的基本特征,允许将类型作为参数传递给另一个类型、函数、或者其他结构。TypeScript 支持泛型作为将类型安全引入组件的一种方式。这些组件接受参数和返回...【详细内容】
2023-11-20  Search: TypeScript  点击:(196)  评论:(0)  加入收藏
TypeScript 5.3 来了,一大波新特性
根据 TypeScript 路线图,TypeScript 5.3 计划于 11 月 14 日发布。下面是该版本带来的新特性: 导入属性 导入类型中稳定支持 resolution-mode 所有模块模式均支持 resolution-...【详细内容】
2023-11-16  Search: TypeScript  点击:(164)  评论:(0)  加入收藏
万字详解 TypeScript 高级用法
TypeScript 是一种类型安全的 JavaScript 超集,除了基本类型和对象类型之外,TypeScript 还提供了一些高级类型系统,使得我们可以更好地处理复杂的数据结构和业务逻辑。本文将深...【详细内容】
2023-10-31  Search: TypeScript  点击:(343)  评论:(0)  加入收藏
不要在Typescript中使用Function类型
原文链接:https://www.totaltypescript.com/dont-use-function-keyword-in-typescript翻译:一川在Typescript中不应该使用Function作为一个类型,因为它可以表示任何函数。通常,...【详细内容】
2023-09-27  Search: TypeScript  点击:(280)  评论:(0)  加入收藏
为什么选择 TypeScript,它有什么优点吗?
在当今快速发展的软件开发领域,TypeScript技术的重要性日益凸显。TypeScript是一种由微软开发的开源编程语言,它扩展了JavaScript,并为开发者提供了强大的静态类型检查。首先,Ty...【详细内容】
2023-09-22  Search: TypeScript  点击:(193)  评论:(0)  加入收藏
掌握TypeScript,开启高质量前端之旅
相信TypeScript对于一个前端开发来讲应该是不陌生的,因为作为一个前端开发者来说,不是在学习如何使用TS就是在去学习TS如何使用的路上。为什么这么说呢?一些技术的发展离不开人...【详细内容】
2023-09-22  Search: TypeScript  点击:(285)  评论:(0)  加入收藏
▌简易百科推荐
关于TypeScript中的泛型,希望这篇文章能让你彻底理解泛型
在我们今天的旅程中,我们一起探索TypeScript中那些令人兴奋的泛型知识。从类型推断的便捷性到泛型在日常编程中的灵活运用,希望这些内容能够帮助你解开围绕泛型的所有迷雾。记...【详细内容】
2024-04-23    今日头条  Tags:TypeScript   点击:(0)  评论:(0)  加入收藏
JavaScript的异步编程常见模式
在JavaScript中,异步编程是一种处理长时间运行操作(如网络请求或I/O操作)的常见方式。它允许程序在等待这些操作完成时继续执行其他任务,从而提高应用程序的响应性和性能。JavaS...【详细内容】
2024-04-12  靳国梁    Tags:JavaScript   点击:(13)  评论:(0)  加入收藏
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  前端新世界  微信公众号  Tags:JavaScript   点击:(10)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践&mdash;&mdash;如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  前端新世界  微信公众号  Tags:JavaScript   点击:(30)  评论:(0)  加入收藏
又出新JS运行时了!JS运行时大盘点
Node.js是基于Google V8引擎的JavaScript运行时,以非阻塞I/O和事件驱动架构为特色,实现全栈开发。它跨平台且拥有丰富的生态系统,但也面临安全性、TypeScript支持和性能等挑战...【详细内容】
2024-03-21  前端充电宝  微信公众号  Tags:JS   点击:(31)  评论:(0)  加入收藏
构建一个通用灵活的JavaScript插件系统?看完你也会!
在软件开发中,插件系统为应用程序提供了巨大的灵活性和可扩展性。它们允许开发者在不修改核心代码的情况下扩展和定制应用程序的功能。本文将详细介绍如何构建一个灵活的Java...【详细内容】
2024-03-20  前端历险记  微信公众号  Tags:JavaScript   点击:(26)  评论:(0)  加入收藏
对JavaScript代码压缩有什么好处?
对JavaScript代码进行压缩主要带来以下好处: 减小文件大小:通过移除代码中的空白符、换行符、注释,以及缩短变量名等方式,可以显著减小JavaScript文件的大小。这有助于减少网页...【详细内容】
2024-03-13  WangLiwen    Tags:JavaScript   点击:(16)  评论:(0)  加入收藏
跨端轻量JavaScript引擎的实现与探索
一、JavaScript 1.JavaScript语言JavaScript是ECMAScript的实现,由ECMA 39(欧洲计算机制造商协会39号技术委员会)负责制定ECMAScript标准。ECMAScript发展史: 2.JavaScript...【详细内容】
2024-03-12  京东云开发者    Tags:JavaScript   点击:(16)  评论:(0)  加入收藏
面向AI工程的五大JavaScript工具
令许多人惊讶的是,一向在Web开发领域中大放异彩的JavaScript在开发使用大语言模型(LLM)的应用程序方面同样大有价值。我们在本文中将介绍面向AI工程的五大工具,并为希望将LLM...【详细内容】
2024-02-06    51CTO  Tags:JavaScript   点击:(61)  评论:(0)  加入收藏
JS小知识,使用这6个小技巧,避免过多的使用 if 语句
最近在重构我的代码时,我注意到早期的代码使用了太多的 if 语句,达到了我以前从未见过的程度。这就是为什么我认为分享这些可以帮助我们避免使用过多 if 语句的简单技巧很重要...【详细内容】
2024-01-30  前端达人  今日头条  Tags:JS   点击:(64)  评论:(0)  加入收藏
站内最新
站内热门
站内头条