ts 泛型的基本使用场景

前言

  • 泛型使用场景:在 定义 函数、接口、类 的时候,不能预先确定要使用的数据的类型,而是 在使用的时候才能确定
  • 将类型作为变量使用,即 动态类型
  • 语法:定义时 <大写字母>,多个逗号隔开,如 <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;
// }

综上,泛型是 ts 中比较重要的一个功能,极大地增强了 ts 的 灵活性,如果不熟练一定要多看多敲

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值