[TOC]
>[success] # 泛型(Generics) 第一部分
![](https://img.kancloud.cn/08/cb/08cb56d19fc956d2ce623b65258b4068_359x297.png)
上面用一张图描述了学习 **TypeScript** 的一个过程, **Adding simple type annotations** 的意思是 **给我们的变量加一些简单的类型声明** ,这个 **滑梯很平滑** ,但是到了 **Generics(泛型)** 就直线下降到 **地狱难度** ,这意味着,我们只要掌握了 **Generics(泛型)** 就把 **typescript** 给攻破了,接下来我们讲一讲 **Generics(泛型)** 是怎么出现的,它是 **为了解决什么问题** ?
>[success] ## 函数使场景问题 demo
举例:现在我们有一个 **函数 echo** ,它只做一件事,我们 **传入一个参数,它把参数返回**
**index.ts**
~~~
function echo (arg){
return arg
}
const result = echo(123)
~~~
**编辑器图片** :
![](https://img.kancloud.cn/e0/0d/e00d3580515c61c36a1e23ed3ced52e5_286x137.png)
上面的代码会出现什么问题呢 ?我们 **传入的数字 123** ,鼠标指向 **result** 时,返回的类型是 **any** ,这 **不是我们想要的结果,这样我们的变量就丧失了类型** ,这不是一个好现象,怎么办呢,我们 **给函数的参数以及返回的参数定义一个 number 类型** ,代码如下:
**index.ts**
~~~
function echo (arg: number): number{
return arg
}
const result = echo(123)
~~~
**编辑器图片**:
![](https://img.kancloud.cn/0f/67/0f67c50bcb1a05812dab8941a6ee7dd6_377x130.png)
但是我们有可能想 **不单单只是想传入 number 类型的参数,有可能是 string、boolean 甚至是复杂类型** ,该怎么办呢,大家可能就会想了,换成 **any** 呗,代码如下:
**index.ts**
~~~
function echo (arg: any): any{
return arg
}
const result = echo(123)
~~~
但是这还不是我们想要的结果,这只能说 **传入的值是 any ,返回的值也是 any** ,但是我们 **传入与返回的值都无法做到统一** ,我们还是 **丧失了类型** ,甚至我们还可以出现一个 **bug** ,还可以把 **result** 常量 **定义成 string** 类型,因为 **echo 返回的是 any 类型,string 属于 any 下的一个类型** ,代码如下:
**index.ts**
~~~
function echo (arg: any): any{
return arg
}
const result:string = echo(123)
~~~
**编辑器图片**:
![](https://img.kancloud.cn/02/5b/025b320cc50b5207c94e0fda7cbf4af0_356x134.png)
但是理论上我们都知道,我们 **传入的 数字,要返回一个 number** ,那么怎么样来解决这个问题呢,这时候 **泛型(Generics)** 就来拯救我们了。
>[success] ## 用泛型(Generics)解决函数问题
**泛型(Generics)** : 在 **定义函数、接口、Class类时,我们不预先指定具体类型,而是在使用时再指定类型**
我们要写 **泛型(Generics)** 首先要找到 **函数名称 echo** , 在 **echo 后面加一对尖括号(<T\>),尖括号里面可以定义泛型(Generics)的名称** ,我这里就叫做 **T** ,这个 **名称叫什么都可以**, **T** 只是一种习惯的写法罢了,这样 **我们就创造了我们第一个泛型的参数** ,相当于 **它创造了一个占位符 ,这个占位符叫 T** ,可以 **把它看做是一个神秘的变量** ,它现在是什么 **类型** 我们不知道,可以是 **任意类型**,在使用时候我会讲到,这时候我们把 **参数的类型** 改成 **T** , **返回值** 也 **修改成 T** ,具体代码如下:
**index.ts**
~~~
function echo<T>(arg: T): T{
return arg
}
const str: string = 'str'
const result = echo(str)
~~~
**编辑器图片**:
![](https://img.kancloud.cn/52/18/5218b1d505123d0d648977f9342f7e53_363x217.png)
传入的是 **string** , **返回** 的也是 **string** ,这样就达到了我们想要的效果,当然我们也可以不给 **str 常量定义类型**,因为它可以自动做 **[类型推论](https://www.kancloud.cn/wangjiachong/vue_notes/2165550)** ,代码如下:
**index.ts**
~~~
function echo<T>(arg: T): T{
return arg
}
const result = echo('str')
~~~
**编辑器图片**:
![](https://img.kancloud.cn/2a/d2/2ad28d3da6c467cb83490043e688c43c_303x127.png)
当然刚才上面说的那个 **bug** 也不会再出现了,如下:
**index.ts**
~~~
function echo<T>(arg: T): T{
return arg
}
const result:string = echo(123)
~~~
**编辑器图片**:
![](https://img.kancloud.cn/b0/27/b0275242ed7c8494c96e9bf905143e23_544x192.png)
>[success] ## 泛型(Generics)传多个值
**需求**: 我们有一个 **[元祖](https://www.kancloud.cn/wangjiachong/vue_notes/2165547#_26)** ,里面有 **2 个值** , **它们的类型都是随意的** ,这时候我们要 **返回一个新的元祖**
**index.ts**
~~~
function swap(tuple){
return [tuple[1], tuple[0]]
}
~~~
像上面这样写,我们就会 **丧失它们的类型** ,我们需要这样写:
**index.ts**
~~~
function swap<T, U>(tuple: [T, U]):[U, T] {
return [tuple[1], tuple[0]] // 注意返回的类型是 [U,T], 不是 [T, U]
}
const result = swap(['string', 123])
~~~
**编辑器图片**:
![](https://img.kancloud.cn/73/c7/73c7f64058e7dcf5cf0aab0444f8dbcb_700x157.png)
这样的话 **result** 的第 **0** 个就是 **number 类型** ,第 **1** 个就是 **string 类型** ,我们可以使用 **对应类型给提供的方法** ,如下图:
![](https://img.kancloud.cn/13/d0/13d01119dcd41d41fc4fab756aea9c71_809x472.png)
- vue 26课
- Vue-cli3.0项目搭建
- Vue-ui 创建cli3.0项目
- Vue-ui 界面详解
- 项目目录详解
- public文件夹
- favicon.ico
- index.html
- src文件夹
- api文件夹
- assets文件夹
- components文件夹
- config文件夹
- directive文件夹
- lib文件夹
- mock文件夹
- mock简明文档
- router文件夹
- store文件夹
- views文件夹
- App.vue
- main.js
- .browserslistrc
- .editorconfig
- .eslintrc.js
- .gitignore
- babel.config.js
- package-lock.json
- package.json
- postcss.config.js
- README.en.md
- README.md
- vue.config.js
- Vue Router
- 路由详解(一)----基础篇
- 路由详解(二)----进阶篇
- Vuex
- Bus
- Vuex-基础-state&getter
- Vuex-基础-mutation&action/module
- Vuex-进阶
- Ajax请求
- 解决跨域问题
- 封装axios
- Mock.js模拟Ajax响应
- 组件封装
- 从数字渐变组件谈第三方JS库使用
- 从SplitPane组件谈Vue中如何【操作】DOM
- 渲染函数和JSX快速掌握
- 递归组件的使用
- 登陆/登出以及JWT认证
- 响应式布局
- 可收缩多级菜单的实现
- vue杂项
- vue递归组件
- vue-cli3.0多环境打包配置
- Vue+Canvas实现图片剪切
- vue3系统入门与项目实战
- Vue语法初探
- 初学编写 HelloWorld 和 Counter
- 编写字符串反转和内容隐藏功能
- 编写TodoList功能了解循环与双向绑定
- 组件概念初探,对 TodoList 进行组件代码拆分
- Vue基础语法
- Vue 中应用和组件的基础概念
- 理解 Vue 中的生命周期函数
- 常用模版语法讲解
- 数据,方法,计算属性和侦听器
- 样式绑定语法
- 条件渲染
- 列表循环渲染
- 事件绑定
- 表单中双向绑定指令的使用
- 探索组件的理念
- 组件的定义及复用性,局部组件和全局组件
- 组件间传值及传值校验
- 单向数据流的理解
- Non-Props 属性是什么
- 父子组件间如何通过事件进行通信
- 组件间双向绑定高级内容
- 使用匿名插槽和具名插槽解决组件内容传递问题
- 作用域插槽
- 动态组件和异步组件
- 基础语法知识点查缺补漏
- Vue 中的动画
- 使用 Vue 实现基础的 CSS 过渡与动画效果
- 使用 transition 标签实现单元素组件的过渡和动画效果
- 组件和元素切换动画的实现
- 列表动画
- 状态动画
- Vue 中的高级语法
- Mixin 混入的基础语法
- 开发实现 Vue 中的自定义指令
- Teleport 传送门功能
- 更加底层的 render 函数
- 插件的定义和使用
- 数据校验插件开发实例
- Composition API
- Setup 函数的使用
- ref,reactive 响应式引用的用法和原理
- toRef 以及 context 参数
- 使用 Composition API 开发TodoList
- computed方法生成计算属性
- watch 和 watchEffect 的使用和差异性
- 生命周期函数的新写法
- Provide,Inject,模版 Ref 的用法
- Vue 项目开发配套工具讲解
- VueCLI 的使用和单文件组件
- 使用单文件组件编写 TodoList
- Vue-Router 路由的理解和使用
- VueX 的语法详解
- CompositionAPI 中如何使用 VueX
- 使用 axios 发送ajax 请求
- Vue3.0(正式版) + TS
- 你好 Typescript: 进入类型的世界
- 什么是 Typescript
- 为什么要学习 Typescript
- 安装 Typescript
- 原始数据类型和 Any 类型
- 数组和元组
- Interface- 接口初探
- 函数
- 类型推论 联合类型和 类型断言
- class - 类 初次见面
- 类和接口 - 完美搭档
- 枚举(Enum)
- 泛型(Generics) 第一部分
- 泛型(Generics) 第二部分 - 约束泛型
- 泛型第三部分 - 泛型在类和接口中的使用
- 类型别名,字面量 和 交叉类型
- 声明文件
- 内置类型
- 总结