TS学习笔记10-高级类型2

164 阅读3分钟

索引类型

从对象中抽取一些属性的值,然后拼接成数组,可以这么写,

const userInfo = { name: 'lin', age: '18', } 
function getValues(userInfo: any, keys: string[]) { 
    return keys.map(key => userInfo[key]) 
} 
// 抽取指定属性的值 
console.log(getValues(userInfo, ['name','age'])) // ['lin', '18'] 
// 抽取obj中没有的属性: 
console.log(getValues(userInfo, ['sex','outlook'])) // [undefined, undefined]

虽然 obj 中并不包含 sexoutlook 属性,但 TS 编译器并未报错

此时使用 TS 索引类型,对这种情况做类型约束,实现动态属性的检查。

理解索引类型,需先理解 keyof(索引查询)T[K](索引访问)extends (泛型约束)

keyof(索引查询)

keyof 操作符可以用于获取某种类型的所有键,其返回类型是联合类型。

interface IPerson {
  name: string;
  age: number;
}

type Test = keyof IPerson; // 'name' | 'age'

T[K](索引访问)

T[K],表示接口 T 的属性 K 所代表的类型

interface IPerson {
  name: string;
  age: number;
}
let type1: IPerson['name'] // string
let type2: IPerson['age']  // number

extends (泛型约束)

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

interface ILength {
    length: number
}

function printLength<T extends ILength>(arg: T): T {
    console.log(arg.length)
    return arg
}
const str = printLength('lin')
const arr = printLength([1,2,3])
const obj = printLength({ length: 10 })

const num = printLength(10) // 报错,Argument of type 'number' is not assignable to parameter of type 'ILength'

改造上面的函数

先定义一个T类型,作为传入的对象的类型,然后使用 keyof T 拿到传入的对象的属性名枚举值的联合,让类型K继承extends keyof T ,这样限制传入的K[]类型的数组的值都是是传入对象的属性名的字符串。T[K]表示传入的对象的传入属性名数组中的属性值类型的联合。

const userInfo = {
  name: 'lin',
  age: '18',
}
// 改造前
function getValues(userInfo: any, keys: string[]) {
  return keys.map(key => userInfo[key])
}
getValues(userInfo, ['name'])
getValues(userInfo, ['name1'])
// 改造后
function getValues2<T, K extends keyof T>(userInfo: T, keys: K[]): T[K][] {
  return keys.map(key => userInfo[key])
}
getValues2(userInfo, ['name'])
getValues2(userInfo, ['id']) // 报错 类型“"id"”不可分配给类型“"name" | "age"”

映射类型

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

in

介绍映射类型之前,先介绍一下 in 操作符,用来对联合类型实现遍历。

type Person = "name" | "school" | "major" 
type Obj = {
  [p in Person]: string
}

image.png

Partial

Partial<T>T的所有属性映射为可选的,例如:

interface User{
    name:string,
    age:number
}
type NewUser = Partial<User>
let p1:User = {}  // 报错  类型“{}”缺少类型“User”中的以下属性: name, age
let p2:NewUser = {} // 不报错

Partial 原理

type MePartial<T> ={
    [p in keyof T]?:T[p]
}

Readonly

Readonly<T>T的所有属性映射为只读的,例如:

interface User{
    name:string,
    age:number
}
type NewUser = Readonly<User>
let p:NewUser = {name:'w',age:1} // 不报错
p.name='z' // 报错 无法为“name”赋值,因为它是只读属性。
p.age = 2 // 报错 无法为“age”赋值,因为它是只读属性。

Readonly 原理

type MeReadonly<T> ={
    readonly [p in keyof T]:T[p]
}

Pick

Pick用于抽取对象子集,挑选一组属性并组成一个新的类型,例如:

interface User{
    name:string,
    age:number,
    id:number
}

type NewUser = Pick<User,'name'|'age'>
let p:NewUser = {name:'w',age:1,id:1}  // 报错 不能将类型“{ name: string; age: number; id: number; }”分配给类型“NewUser”。对象字面量只能指定已知属性,并且“id”不在类型“NewUser”中

Pick 原理

type Pick<T, K extends keyof T> = { [P in K]: T[P] }

Record

上面三种映射类型官方称为同态,意思是只作用于 obj 属性而不会引入新的属性。

Record 是会创建新属性的非同态映射类型。

interface User{
    name:string,
}

type NewUser = Record<string,User>
let p:NewUser = {
    p1:{name:'w'}
}  

Record 原理

type MeRecord<T extends keyof any,K> = {
    [p in T]:K
}

条件类型

Exclude

Exclude 意思是不包含,Exclude<T, U> 会返回 联合类型 T 中不包含 联合类型 U 的部分。

image.png

Exclude 原理

type Exclude<T, U> = T extends U ? never : T

Extract

Extract<T, U>提取联合类型 T 和联合类型 U 的所有交集。

image.png

image.png

Exclude 原理

type Extract<T, U> = T extends U ? T:never

工具类型(Utility Types)

Omit

Omit<T, U>从类型 T 中剔除 U 中的所有属性。

image.png

Omit 原理

type MeOmit<T, U extends keyof any> = {
  [P in Exclude<keyof T, U>]: T[P]
}

NonNullable

NonNullable<T> 用来过滤类型中的 null 及 undefined 类型。

image.png

Parameters

Parameters 获取函数的参数类型,将每个参数类型放在一个元组中

type T1 = Parameters<() => string> // [] 
type T2 = Parameters<(arg: string) => void> // [name:string] 
type T3 = Parameters<(arg1: string, arg2: number) => void> // [arg1: string, arg2: number]

ReturnType

ReturnType 获取函数的返回值类型。

type T0 = ReturnType<() => string> // string 
type T1 = ReturnType<(s: string) => void> // void