Skip to content

索引类型

typescript
let adObj = {
  a: 1,
  b: 2,
  c: 3,
}
// 获取对象中的指定属性的值集合
function getValues(adObj: any, keys: string[]) {
  return keys.map((key) => adObj[key])
}
console.log(getValues(adObj, ['a', 'b'])) // [1, 2]
console.log(getValues(adObj, ['e', 'f'])) // [undefined, undefined]

虽然obj中并不包含e, f属性,但TS编译器并未报错 此时使用TS索引类型,对这种模式做类型约束

概念

索引类型的查询操作符

keyof T :表示类型T的所有公共属性的字面量的联合类型

typescript
interface obj {
  a: number
  b: string
}
let key: keyof obj

image.png

索引访问操作符

T[K] :表示对象T的属性K所代表的类型

typescript
interface obj {
  a: number
  b: string
}
let value: obj['a']

image.png

泛型约束

T extends U :表示泛型变量可以通过继承某个类型,获得某些属性

索引类型

约束目的:从 obj 对象中抽取的属性数组 keys 中的元素,一定得是 obj 对象中的属性 思路:

  1. 定义泛型变量T, K,分别约束obj对象和keys数组
  2. 为K增加一个泛型约束,使K继承Obj的所有属性的联合类型, 即K extends keyof T
  3. 此时,函数返回值类型-数组的元素类型,就是属性K对应的数组,即T[K][]
typescript
let adObj = {
  a: 1,
  b: 2,
  c: 3,
}

function getValues<T, K extends keyof T>(adObj: T, keys: K[]): T[K][] {
  return keys.map((key) => adObj[key])
}

console.log(getValues(adObj, ['a', 'b'])) // [1, 2]
console.log(getValues(adObj, ['e', 'f']))

image.png

索引类型可以约束对象属性的查询和访问 配合泛型约束能够建立对象,对象属性,属性值之间的约束关系

映射类型

TS允许将一个类型映射成另外一个类型

typescript
// 定义接口 Obj
interface Obj {
  a: string
  b: number
  c: boolean
}

// 使用类型别名定义类型ReadonlyObj
type ReadonlyObj = Readonly<Obj> // Readonly是TS内置的泛型接口

ReadonlyObj与Obj成员完全相同,区别是ReadonlyObj中的成员属性均为只读

image.png

Readonly的实现原理

Readonly是一个可索引类型的泛型接口

  • 索引签名为P in keyof T :

其中 keyof T就是一个一个索引类型的查询操作符,表示类型T所有属性的联合类型

  • P in :

相当于执行了一个 for in 操作,会把变量P依次绑定到T的所有属性上

  • 索引签名的返回值就是一个索引访问操作符 : T[P]  这里代表属性P所指定的类型
  • 最后再加上Readonly就把所有的属性变成了只读,这就是 Readonly 的实现原理

其他的映射类型

可选和只读映射类型的实现几乎一样, 只读属性变为可选

typescript
type PartialObj = Partial<Obj> // 可选

image.png

抽取接口Obj中的属性a和b,形成新类型

typescript
type PickObj = Pick<Obj, 'a' | 'b'>

image.png

Pick映射类型的实现原理

Pick映射类型有两个参数: 第一个参数T,表示要抽取的目标对象 第二个参数K,具有一个约束:K一定要来自T所有属性字面量的联合类型,即映射得到的新类型的属性一定要从K中选取 以上三种映射类型官方称为同态,意思是只作用于obj属性而不会引入新的属性

引入新属性的非同态映射类型

第一个参数是预定义的新属性,如x,y 第二个参数就是已知类型

typescript
type RecordObj = Record<'x' | 'y', Obj>

image.png

映射出的新类型所具有的属性由Record的第一个属性指定,而这些属性类型为第二个参数指定的已知类型,这种类型就是一个非同态的类型 映射类型本质上是一种预先定义的泛型接口,通常还会结合索引类型,获取对象的属性和属性值,从而将一个对象映射为我们想要的结构

条件类型

条件类型是一种由条件表达式所决定的类型 条件类型使类型具有了不唯一性,同样增加了语言的灵活性

声明: T extends U ? X : Y 若类型T可被赋值给类型U,那么结果类型就是X类型,否则就是Y类型

typescript
type TypeName<T> = T extends string
  ? 'string'
  : T extends number
  ? 'number'
  : T extends boolean
  ? 'boolean'
  : T extends undefined
  ? 'undefined'
  : T extends Function
  ? 'Function'
  : 'object'

type T1 = TypeName<string>
type T2 = TypeName<string[]>

image.png

image.png

分步式条件类型

当类型T为联合类型时: T为类型A和类型B的联合类型,结果类型会变成多个条件类型的联合类型  (A | B) extends U ? X : Y

可以将A和B进行拆解: (A extends U ? X : Y) | (B extends U ? X : Y)

typescript
type TypeName<T> = T extends string
  ? 'string'
  : T extends number
  ? 'number'
  : T extends boolean
  ? 'boolean'
  : T extends undefined
  ? 'undefined'
  : T extends Function
  ? 'Function'
  : 'object'

type T3 = TypeName<string | string[]>

image.png

分步式条件类型的应用

typescript
// 如果T可以被赋值给U,结果类型为never类型,否则为T类型
type Diff<T, U> = T extends U ? never : T

type T4 = Diff<'a' | 'b' | 'c', 'a' | 'e'>

// 拆解分析:
// Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e">
// never | "b" | "c"
// "b" | "c"

image.png

先判断a是否可以被赋值给这个字面量联合类型'a' | 'e',答案是可以的,所以返回never 继续,因为b不可以被赋值给字面量联合类型'a' | 'e',所以返回b 继续,c不可以被赋值给'a' | 'e',所以返回c 最后,never和b,c的联合类型为'b' | 'c'

Diff类型作用:可以从类型T中过滤掉可以被赋值给类型U的类型 也可以实现从类型T中移除不需要的类型,如undefined和null

定义一个NotNull,从T中过滤掉undefined和null

typescript
// Diff扩展:从T中过滤掉undefined和null
type NotNull<T> = Diff<T, undefined | null>

// 过滤掉undefined和null,T5的类型就变成了string和number
type T5 = NotNull<string | number | undefined | null>

image.png

typescript
// Diff 的内置类型叫做 Exclude<T, U>
// NotNull 的内置类型叫做NonNullable<T>
// Extract<T, u>

// Extract 和 Exclude 相反
// Exclude 作用是从类型T中过滤掉可以赋值给类型U的类型
// Extract 作用是可以从类型T中抽取出可以赋值给U的类型

type T6 = Extract<'a' | 'b' | 'c', 'a' | 'e'>
type T7 = Exclude<'a' | 'b' | 'c', 'a' | 'e'>

image.png

image.png

typescript
// ReturnType<T>:可获取函数返回值类型
type T8 = ReturnType<() => string>

image.png

typescript
// 源码

/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R
  ? R
  : any

// 分析
// T extends (...args: any) => any:
// ReturnType要求参数T可以赋值给一个函数,这个函数有任意的参数,返回值类型也是任意的
// 由于函数返回值类型不确定,这里使用了infer关键字,表示待推断,延迟推断,需要根据实际的情况确定

// infer R ? R : any:
// 如果实际类型是R,那么结果类型就是R,否则返回值类型就是any