TypeScript-泛型-2-泛型约束

前端人

共 4520字,需浏览 10分钟

 ·

2021-08-12 18:28

鬼哥本周将通过几篇优秀的Typescript文章,让大家学习到Typescript一些高级的用法,让大家对Typescript更加的深入理解,并且更好实践到工作当中,【一共五篇文章】,关注我们一起完成这个系列的学习

原文:https://github.com/leslie1943




🚀 泛型约束?

有时我们可能希望限制每个类型变量接收的类型数量,这就是泛型约束的作用.

💛 4.1 确保属性存在
  • 希望类型变量对应的类型上存在某些属性. 这时, 除非我们显式地将特定属性定义为类型变量, 否则编译器不会知道他们的存在.
  • 一个很好的例子是在处理字符串或数组时, 我们会假设 length 属性是可用的. 让我们再次使用 identity 函数并尝试输出参数的长度.
function identity<T>(arg: T): T {
  console.info(arg.length)
  return arg
}
  • 在这种情况下, 编译器将不会知道 T 确实含有 length 属性, 尤其是在可以将任何类型赋给类型变量 T 的情况下.我们需要做的就是让类型变量 extends 一个含有我们所需属性的接口, 比如这样
interface Length {
  length: number
}
function identity<T extends Length>(arg: T): T {
  console.info(arg.length)
  return arg
}
  • T extends Length 用于告诉编译器, 我们支持已经实现 Length 接口的任何类型. 之后, 当我们使用不含有 length 属性的对象作为参数调用 identity 函数时, TypeScript 会提示相关的错误信息:
// identity(52) 
// Argument of type 'number' is not assignable to parameter of type 'Length'.ts(2345)
  • 此外, 我们还可以使用 , 号来分隔多种约束类型, 比如:<T extends Length, Type2, Type3>. 而对于上述的 length 属性问题来说, 如果我们显式地将变量设置为数组类型, 也可以解决该问题, 具体方式如下
function identity2<T>(arg: T[]): T[] {
  console.info(arg.length)
  return arg
}
identity2([123]) // function identity2<number>(arg: number[]): number[]
identity2([`1``2``3`]) // function identity2<string>(arg: string[]): string[]

// 或者

function identity3<T>(arg: Array<T>): Array<T{
  console.info(arg.length)
  return arg

💛 4.2 检查对象上的键是否存在

  • 泛型约束的另一个常见的使用场景就是检查对象上的键是否存在. 不过在看具体示例之前, 我们得来了解一下 keyof 操作符, keyof 操作符是在 TypeScript 2.1 版本引入的, 该操作符可以用于获取某种类型的所有键, 其返回类型是联合类型. 举个 keyof 的使用示例:
interface Person {
  name: string
  age: number
  location: string
}

type K1 = keyof Person // 'name' | 'age' | 'location'
type K2 = keyof Person[] // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person } // string | number
  • 通过keyof操作符, 我们就可以获取指定类型的所有键, 之后我们就可以结合前面介绍的extends约束,限制输入属性名包含在keyof返回的联合类型中.
interface Person {
  name: string
  age: number
  location: string
}

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

const person: Person = {
  name: 'su',
  age: 22,
  location: 'dalian',
}

// function getProperty<Person, "age">(obj: Person, key: "age"): number
console.info('getProperty(person, "age")', getProperty(person, 'age')) 

// function getProperty<Person, "name">(obj: Person, key: "name"): string
console.info('getProperty(person, "name")', getProperty(person, 'name'))
  • 在以上的 getProperty 函数中, 我们通过 K extends keyof T 确保参数 key 一定是对象中含有的键, 这样就不会发生运行时错误. 这是一个类型安全的解决方案, 与简单调用 let value = obj[key] 不同.
demo
enum Difficulty {
  Easy,
  Medium,
  Hard,
}

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

let tsInfo = {
  name: 'TypeScript',
  supersetOf: 'JavaScript',
  difficulty: Difficulty.Medium,
}

let difficulty: Difficulty = getProperty(tsInfo, 'difficulty'// OK
let name: string = getProperty(tsInfo, 'name'// OK
// let supersetof: string = getProperty(tsInfo, 'superset_of') 
// ❌ Argument of type '"superset_of"' is not assignable to parameter of type '"difficulty" | "name" | "supersetOf"'.ts(2345)
  • 很明显通过使用泛型约束, 在编译阶段我们就可以提前发现错误, 大大提高了程序的健壮性和稳定性.

关注公众号添加鬼哥微信,和鬼哥一起学习

❤️ 看完三件事

如果你觉得这篇内容对你挺有启发,不妨:

  • 点个【在看】,或者分享转发,让更多的人也能看到这篇内容

  • 点击↓面关注我们,一起学前端

  • 长按↓面二维码,添加鬼哥微信,一起学前端



浏览 18
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报