TypeScript学习 | 泛型

本文介绍了JavaScript中的泛型概念,包括其在函数、接口和类中的应用,如何通过类型参数实现类型安全,以及单/多类型参数的使用、泛型约束和常见类型工具的运用实例。

简介

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

作用

可以保证类型安全的前提下,让函数、接口或类与多种类型一起工作,从而实现复用

基本使用

举个例子:

创建一个id函数、该函数传入什么数据就返回数据本身,也就是参数和返回值类型相同。

function id(value:number): number {
  return value
}

id<number>(99) // 99
function id(value:string): string {
  return value
}

id<string>('hhh') // hhh

综上所述、上面两个函数接收的类型是写死的,只能传入相应的数据类型,为了能让函数接口任意类型吗,可以将参数类型改为any。但是这样就失去TS的类型保护,类型不安全。

这时候,泛型就能派上用场了。

function id<T>(value:T):T {
  return value
}

id<number>(99) // 99
id<string>('hhh') // hhh
id<number[]>([1,2,3]) // [1,2,3]
  • 在函数名称的后面添加<>,其中尖括号里面的T就是类型参数,它用来处理类型而不是值,它可以是任意合法的变量名称
  • 因为T是类型,因此可以将T作为参数和返回值的类型,表示参数和返回值具有相同的类型
  • 由于TS内部会采用一种叫做类型参数推断的机制,来根据传入的数据推断出相对应的类型,针对一些基础数据类型,可以省略不写。比如上述 id<number>(99)可直接写成id(99)id<string>('hhh')可简单写成id('hhh')。当编译器无法推断类型或者推断的类型不准确时,就需要显式的传入类型参数。比如id<number[]>([1,2,3])

单个类型参数 VS 多个类型参数

单个类型参数

function id<T>(value:T):T {
  return value
}
id<number>(99) // 99

顾名思义:类型参数只有一个,如上,只有一个 T

多个类型参数

function swap<T, U>(value: [T, U]): [U, T] {
    return [value[1], value[0]];
}

swap([18, 'age']); // ['age', 18]

多个类型参数之间可进行相互约束

多类型变量之间还可以进行约束,比如第二个类型变量所第一个类型变量约束。

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

getPro({name:'abc',age:18},'age')
getPro('abc','length')
getPro([a,b,c],'length')

  • keyof 接收一个对象类型,生成其键名称或数字的联合类型
  • 类型K受类型T的约束,上述可以理解为K只能是T中所存在的键中任意一个,或者说只能访问对象中存在的属性。

泛型约束

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以导致无法访问它的属性和方法。如下:

function id<T>(value:T):T {
  console.log(value.length) //这段代码编译器会报错:Property 'length' does not exist on type 'T'. 
  return value
} 

上述代码中无法保证传入的数据一定存在length属性。所以,就需要为泛型添加约束。(只允许这个函数传入那些包含 length 属性的变量)

指定更加具体的类型

function id<T>(value:T[]):T[] {
  console.log(value.length)  
  return value
} 

如上、可将T改成T[],也就是T类型的数组,这样数组就一定存在lengh属性,就可以正常访问了。

使用 extends 添加约束


interface TLength {
  length:number
}

function id<T extends TLength>(value:T): T {
   console.log(value.length)  
   return value
}


id(['xxx','yyy'])
id('hhhhh')
id({length:10,name:'abcd'})
  • 创建描述约束的接口TLength,该接口提供length属性
  • 通过extends关键字使用该接口,为泛型添加约束。

泛型接口

接口也可以配合泛型使用

image.png

泛型类

image.png

image.png

泛型工具类型

Partial<Type>

用来创建一个类型,将Type中的所有属性都设置为可选

image.png

image.png

Readonly<Type>

用来创建一个类型,将Type中的所有属性都设置为只读

image.png

Pick<Type,Keys>

从Type中选择一组属性来构造新类型

image.png

image.png

Record<Keys,Type>

构造一个对象类型,属性键为Keys,属性类型为Type

image.png

image.png

<think> 首先,用户的问题是:“我想了解TypeScript的定义”。这是基于之前提供的引用内容,但指令说“上述引用仅供参考”,所以我会专注于回答用户的问题,同时参考引用但不完全依赖它们。 关键点: - 用户想了解TypeScript的定义。 - 根据系统指令,我必须使用中文回答。 - 回答需要结构清晰,逐步解答。 - 确保LaTeX正确:行内数学用$...$,独立公式用$$...$$单独成段。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 最后生成相关问题。 用户的问题是关于TypeScript的定义。从引用中提取关键信息: - 引用[1]:允许在定义时不指定具体类型,使用时传入类型,使代码更灵活。 - 引用[2]:使类型设置更灵活,但需要约束来处理问题。 - 引用[3]:展示函数的使用示例。 - 引用[4]:基本函数示例。 的核心定义:是一种编程概念,在TypeScript中,它允许你创建可重用的组件,这些组件可以处理多种类型,而不是一个特定类型。你可以定义一个函数、接口或类时使用类型变量(如T),然后在实例化时指定具体类型。 结构化回答: 1. **定义**:清晰阐述什么是。 2. **目的**:解释为什么使用(灵活性、代码重用)。 3. **基本语法**:通过示例说明。 4. **约束**:提到约束的重要性(如引用[2]中的问题)。 5. **引用标识**:自然地添加引用到相关部分。 回答结构: - 开头直接回答问题。 - 逐步解释。 - 结束生成相关问题。 最后,生成相关问题: - 基于回答内容,提出3-5个相关问题。 - 指令说“§§相关问题§§”,然后列出问题。 确保LaTeX正确: - 例如,提到类型变量时可以用$T$。 - 如果有方程,用$$...$$,但这里可能不需要独立公式。 完整回答: 1. 回答部分:定义。 2. 相关问题部分。 草拟回答: "在TypeScript中,(Generics)是一种强大的工具,允许我们在定义函数、接口或类时使用类型参数来代替具体的类型。这意味着你可以创建一个组件,其类型在定义时不确定,而是在使用时指定。的目的是提高代码的灵活性和重用性,避免为每种可能的类型编写重复的代码。 例如,一个简单的函数定义如下: ```typescript function identity<T>(arg: T): T { return arg; } ``` 这里,<T> 表示一个类型变量。当你调用这个函数时,比如 identity<number>(42),类型变量 T 被指定为 number,确保函数处理数值类型TypeScript类型推断也能自动识别类型,如 identity('hello') 推断 T 为 string。 使用时,有时需要约束类型参数以确保某些属性可用。例如,确保类型有 length 属性: ```typescript function lengthOf<T extends { length: number }>(obj: T): number { return obj.length; } ``` 这里,T extends { length: number } 约束 T 必须有一个 length 属性,类型为 number[^2]。 在实际应用中,广用于创建可重用的库函数、接口定义等[^1][^3][^4]。" 添加引用标识: - 在提到的概念末尾自然地添加,如[^1]来自引用1。 相关问题: 1. 如何在TypeScript中约束类型? 2. 接口和类的定义有什么区别? 3. TypeScript中的高级用法有哪些? 4. 使用时常见的问题和解决方法是什么? 确保回答真实可靠:基于TypeScript文档知识,引用提供的内容。 </think> ### TypeScript的定义 在 TypeScript 中,**(Generics)** 是一种强大的编程机制,允许你在定义函数、接口或类时不预先指定具体的类型,而是使用一个**类型变量**(通常表示为 $T$、$U$ 等)作为占位符。这个类型变量在组件使用时才被传入具体的类型的核心目的是提高代码的灵活性、重用性和类型安全性,避免为不同数据类型重写类似逻辑[^1]。 #### 核心定义和特点 1. **基本概念**: 通过类型变量(如 $T$)实现“参数类型”。在定义阶段,$T$ 代表未知类型;在使用阶段,$T$ 被替换为实际类型(如 `number` 或 `string`)。例如: ```typescript function identity<T>(arg: T): T { return arg; } ``` 这里,$T$ 是一个类型参数。调用时,`identity<number>(10)` 会将 $T$ 绑定为 `number`,确保输入和输出类型一致[^4]。 2. **目的和优势**: - **灵活性**:处理多种数据类型无需重复代码,例如一个函数可同时处理数字和字符串[^1]。 - **类型安全**:TypeScript 编译器在编译时检查类型错误,避免运行时问题(如错误访问属性)[^2]。 - **代码重用**:减少冗余,例如通用工具函数或数据结构(如数组处理)[^3]。 3. **关键特性**: - **类型约束(Constraints)**:类型变量可以被约束,以确保它具有某些属性。例如,强制 $T$ 必须包含 `length` 属性: ```typescript function getLength<T extends { length: number }>(obj: T): number { return obj.length; // 安全访问,因为 T 一定有 length } ``` 如果不使用约束(如 `T extends { length: number }`),编译器会报错,因为不是所有类型(如 `number`)都有 `length` 属性[^2]。 - **多类型参数**:支持多个类型变量(如 $T$ 和 $U$),用于复杂场景: ```typescript function combine<T, U>(a: T, b: U): string { return `${a}${b}`; } ``` 示例中,`combine<number, string>(1, "test")` 返回字符串[^3]。 #### 为什么需要 在 JavaScript 中,变量类型是动态的,但 TypeScript 强调静态类型检查。填补了这一差距:它允许开发者编写通用代码,同时保留类型系统的严格性。如果没有,你可能需要为不同类型编写多个相似函数(如 `numberIdentity` 和 `stringIdentity`),这会增加维护成本[^1][^4]。 总之,TypeScript 类型系统的核心工具,它通过类型抽象提升代码的通用性和健壮性。实际应用中,它常用于库开发(如 RxJS)、数据处理函数和组件设计[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值