前言
- 泛型使用场景:在
定义
函数、接口、类
的时候,不能预先确定要使用的数据的类型,而是在使用的时候才能确定
将类型作为变量使用
,即动态类型
- 语法:定义时
<大写字母>
,多个逗号隔开,如 <T, K, V>
泛型在各中的使用
1、函数 使用泛型
- 函数定义时,
参数
或返回值类型
不确定 function name <T> () {}
例1、简单使用
function test<T>(x: T, y: T): T[] {
return [x, y]
}
// 会自动做类型判断,调用时不写类型也行
test<number>(1, 2)
test(1, 2)
test('a', 'b')
test(false, true)
例2、 多个泛型参数、泛型设置默认值
- 多个泛型之间
,
隔开 - 可以
定义默认值
,如<T = number>
function getArr <K = number, V = string> (value1: K, value2: V) :[K, V] {
return [value1, value2]
}
const arr = getArr<string, number>('jack', 123.321)
console.log('arr', arr) // ['jack', 123.321]
// split、toFixed有代码补全
console.log(arr[0].split('')) // ['j', 'a', 'c', 'k']
console.log(arr[1].toFixed(2)) // 123.32
2、类型别名type 使用泛型
- 类型别名使用
type
关键字来定义 type name<T> = ...
type A<T> = T
let a:A<boolean> = true
let b:A<undefined> = undefined
let c:A<null> = null
let d:A<string> = 'abc'
3、接口interface 使用泛型
定义
接口时,为接口中的属性
或方法
定义泛型类型interface name <T> {}
// 定义一个接口
interface IBlue <T> {
msg: T
}
// 1、在类中使用
class blue implements IBlue<string> {
msg:string = '111'
}
// 2、普通对象的使用
let data: IXiaoman<boolean> = {
msg: false
}
4、类class 使用泛型
- 定义一个类,类中的
属性值的类型
不确定,或方法中的参数
及返回值的类型
不确定 - 在
实例化
类的时候,再确定泛型的类型 class name <T> {}
// 例子:
class Methods <T> {
defaultValue: T
constructor (defaultValue: T) {
this.defaultValue = defaultValue
}
sayHi (msg: T) {
console.log(msg, this.defaultValue)
}
}
// 在实例化类的时候,再确定泛型的类型
// 传入number型
const m1 = new Methods<number>(100)
m1.sayHi(100)
// m1.sayHi('abc') // 报错
// 传入string型
const m2 = new Methods<string>('abc')
m2.sayHi('abc')
// m2.sayHi(100) // 报错
5、泛型约束
- 需要
对泛型
增加一些约束条件
时(约束,想到 interface) - 语法:
泛型 extends 接口
- 进阶搭配:
keyof
可取出对象中所有属性,in
循环
通过几个小例子来理解一下
例1:定义一个函数,返回参数的 length
不是每种类型的参数都有 length 属性,所以我们要 手动限制
当前的 泛型拥有 length 属性
// 定义一个接口,含有 length 属性
interface ILength {
length: number
}
// <T extends ILength>:则 T 必须具有 length 属性
function getLength <T extends ILength> (x: T):number {
return x.length
}
// 字符串有 length 属性
console.log(getLength('aaaaaa')) // 6
// 数组有 length 属性
console.log(getLength([123, 456, 789])) // 3
// 数值没有 length 属性,报错
// console.log(getLength(123))
例2:定义一个函数,传入对象和 key,返回value
① 简单的写法:仅限制 T 为 object 类型
function getVal <T extends object, K> (obj: T, key: K) {
return obj[key] // 报错 Type 'K' cannot be used to index type 'T'
}
报错内容:类型“K”不能用于索引类型“T”,或者说,不能完全确定 K 是 T 中的一个key
。
那怎么能手动限制 K 一定是 T 的 key 呢?
② 进阶写法:使用 keyof
K extends keyof T
,表示拿出 T 中所有的 key 值,作为联合类型 K
// 对泛型 K做出限制(取出T中所有属性,作为一个联合类型,然后让 K extends 它)
function getVal <T extends object, K extends keyof T> (obj: T, key: K) {
return obj[key]
}
let obj = {
name: 'blue',
age: 15
}
console.log(getVal(obj, 'age'))
// console.log(getVal(obj, 'sex')) // 不存在的key,报错
例3:写一个小工具,让 interface 的每一个属性都变成可选属性(?:)
interface IPer {
name: String
age: number
sex: string
}
使用 keyof
取出所有的 key, 使用 in
循环
type OPtions <T extends object> = {
[key in keyof T] ?: T[key]
}
type a = Options<IPer>
// 光标放到 a 上 ,显示如下:
// type a = {
// name?: String;
// age?: number;
// sex?: string;
// }
同样的,也可以都改成 readonly 形式
type Readonly <T extends object> = {
readonly [key in keyof T] : T[key]
}
type b = Readonly<IPer>
// 光标放到 b 上 ,显示如下:
// type b = {
// readonly name: String;
// readonly age: number;
// readonly sex: string;
// }