索引类型
从对象中抽取一些属性的值,然后拼接成数组,可以这么写,
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 中并不包含 sex 和 outlook 属性,但 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
}
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 的部分。
Exclude 原理
type Exclude<T, U> = T extends U ? never : T
Extract
Extract<T, U>提取联合类型 T 和联合类型 U 的所有交集。
Exclude 原理
type Extract<T, U> = T extends U ? T:never
工具类型(Utility Types)
Omit
Omit<T, U>从类型 T 中剔除 U 中的所有属性。
Omit 原理
type MeOmit<T, U extends keyof any> = {
[P in Exclude<keyof T, U>]: T[P]
}
NonNullable
NonNullable<T> 用来过滤类型中的 null 及 undefined 类型。
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