目录
- 介绍
- keyof 类型运算符
- typeof 类型运算符
- 通过索引获得类型
- Conditional Types 条件类型(重点)
- infer 关键字 (重中之重)
- 在条件类型中,如果入参是联合类型,则会被拆解为一个个独立的(原子)类型(成员),然后再进行类型运算。
- as 和 in 关键字 在 循环里 配合 条件语句使用
- Mapping Modifiers 映射修改器(重点)
- Template Literal Types 模板文字类型
- 总结
介绍
ts 中 有一组关键字 (keyof typeof infer extends in as ) 用于利用已有的类型生成新的类型。
ts中知识比较多, 这里只介绍个人觉得日常开发需要用到的部分。
先来个开胃菜 下面这个类型的作用
type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {[P in keyof T as P extends K ? never : P]: T[P];
} & {[P in K]?: T[P];
};interface User {name: string;age: number;email: string;
}
// 示例用法
type PartialUser = PartialByKeys<User, "name" | "age">;const user = {email: "123",
};
作用就是 将类型指定的键的属性变为可选属性
学完这一篇,你就能随性所欲的来封装属于你的类型工具!
keyof 类型运算符
keyof 关键字用于获取一个对象的所有可枚举属性的键的类型
type People = {name:string;age:number;
}type DD = keyof People // "name" | "age"type ArrayObj = { [n: number]: unknown };
type A = keyof ArrayObj;// numbertype MapObj = { [k: string]: boolean };
type M = keyof MapObj; // string | number
// M 是 string | number — 这是因为 JavaScript 对象键始终强制转换为字符串, obj[0] 等价于 obj["0"]// 特殊 用途type B = keyof any //string | number | symbol
typeof 类型运算符
使用 typeof 运算符来获取一个变量或表达式的类型
// 获取基本类型的类型
let myNumber: number = 10;
type MyNumberType = typeof myNumber; // numberlet myString: string = "Hello, World!";
type MyStringType = typeof myString; // string// 获取对象和数组的类型
let myObject = { key: "value" };
type MyObjectType = typeof myObject; // {key:string}let myArray = [1, 2, 3];
type MyArrayType = typeof myArray; // number[]// 获取函数的类型
function myFunction() {}
type MyFunctionType = typeof myFunction; // ()=> void
通过索引获得类型
可以使用索引访问类型来查找另一种类型的特定属性
type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"];// numbertype I1 = Person["age" | "name"];type I1 = string | numbertype I2 = Person[keyof Person];type I2 = string | number | booleantype AliveOrName = "alive" | "name";
type I3 = Person[AliveOrName];type I3 = string | boolean
使用 number 来获取数组元素的类型
type PersonType = Array<string|number>type Person = PersonType[number] // string|number
Conditional Types 条件类型(重点)
Conditional Types 形式看起来有点像 js 中的条件表达式 ( condition ? trueExpression : falseExpression )
ts 主要使用 extends 关键字
interface IdLabel {id: number /* some fields */;
}
interface NameLabel {name: string /* other fields */;
}// 意思是 接受一个类型 被限制在 number 或者 string 如何 是 number 就 返回 IdLabel 类型 string 就返回 NameLabel
type NameOrId<T extends number | string> = T extends number? IdLabel: NameLabel;type a = NameOrId<number> // IdLabeltype b = NameOrId<string> // NameLabel
infer 关键字 (重中之重)
ts 2.8 版本 新增 关键字 infer. 用于在extends的条件语句中推断待推断的类型。
参考链接:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html
// 使用infer来推断函数的返回值类型
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : T;type func = () => number;
type variable = ()=>string;
type funcReturnType = MyReturnType<func>; // funcReturnType 类型为 number
type varReturnType = MyReturnType<variable>; // varReturnType 类型为 string
infer 可以理解为占位符
type Unpacked<T> = T extends (infer U)[]? U: T extends (...args: any[]) => infer U? U: T extends Promise<infer U>? U: T;type T0 = Unpacked<string>; // string
type T1 = Unpacked<string[]>; // string
type T2 = Unpacked<() => string>; // string
type T3 = Unpacked<Promise<string>>; // string
type T4 = Unpacked<Promise<string>[]>; // Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string
协变位置中同一类型变量的多个候选者如何导致推断出联合类型
type ElementOf<T> = T extends Array<infer R> ? R : never;type TTuple = [string, number];
type Union = ElementOf<TTuple>; // string | number
逆变位置中同一类型变量的多个候选会导致推断交集类型
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }? U: never;
type T1 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string/*** {age: number;
} & {address: string;
}*/
type T2 = Bar<{ a: (x: {age:number}) => void; b: (x: {address:string}) => void }>;
在条件类型中,如果入参是联合类型,则会被拆解为一个个独立的(原子)类型(成员),然后再进行类型运算。
type Diff<T, U> = T extends U ? never : T; // Remove types from T that are assignable to U
type Filter<T, U> = T extends U ? T : never; // Remove types from T that are not assignable to U
type T0 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T1 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T2 = Diff<string | number | (() => void), Function>; // string | number
type T3 = Filter<string | number | (() => void), Function>; // () => void
条件类型与映射类型结合使用
type FunctionPropertyNames<T> = {[K in keyof T]: T[K] extends Function ? K : never;}[keyof T];type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;type NonFunctionPropertyNames<T> = {[K in keyof T]: T[K] extends Function ? never : K;}[keyof T];type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;interface Part {id: number;name: string;subparts: Part[];updatePart(newName: string): void;}type T0 = FunctionPropertyNames<Part>; // "updatePart"type T1 = NonFunctionPropertyNames<Part>; // "id" | "name" | "subparts"type T2 = FunctionProperties<Part>; // { updatePart(newName: string): void }type T3 = NonFunctionProperties<Part>; // { id: number, name: string, subparts: Part[] }
as 和 in 关键字 在 循环里 配合 条件语句使用
as 是 4.1 版本后才能使用
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html
作用: 在 as 后面 遍历的变量类型 可以配合 条件语句使用
//那么如何定义一个 ConditionalPick 工具类型,支持根据指定的 Condition 条件来生成新的类型,对应的使用示例如下:
interface Example {a: string;b: string | number;c: () => void;d: {};
}type ConditionalPick<T,K extends T[keyof T]> = {[P in keyof T as T[P] extends K ? P:never ]: T[P]
}
// 测试用例:
type StringKeysOnly = ConditionalPick<Example, string>;
//=> {a: string}
Mapping Modifiers 映射修改器(重点)
在映射过程中可以应用两个附加修饰符: readonly 和 ? ,它们分别影响可变性和可选性。可以通过添加 - 或 + 前缀来删除或添加这些修饰符。如果不添加前缀,则假定为 + 。
例子 : 去除 readonly 只读
type CreateMutable<Type> = {-readonly [Property in keyof Type]: Type[Property];
};type LockedAccount = {readonly id: string;readonly name: string;
};type UnlockedAccount = CreateMutable<LockedAccount>;
去除 ? 可选
type Concrete<Type> = {[Property in keyof Type]-?: Type[Property];};type MaybeUser = {id: string;name?: string;age?: number;};type User = Concrete<MaybeUser>;
在 TypeScript 4.1 及更高版本中,可以使用映射类型中的 as 子句重新映射映射类型中的键
type Getters<Type> = {[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};interface Person {name: string;age: number;location: string;
}type LazyPerson = Getters<Person>;
可以通过条件类型生成 never 来过滤键(重点)
去除 kind 属性
// Remove the 'kind' property
type RemoveKindField<T> = {[P in keyof T as P extends 'kind' ? never : P]: T[P]
};interface Circle {kind: "circle";radius: number;
}type KindlessCircle = RemoveKindField<Circle>;
Template Literal Types 模板文字类型
type World = "world";
type Greeting = `hello ${World}`; //"hello world"
开发中的应用
限制 方法入参的类型规则
type PropEventSource<Type> = {on<Key extends string & keyof Type>(eventName: `${Key}Changed`,callback: (newValue: Type[Key]) => void): void;
};function makeWatchedObject<Type extends { [p: string]: any }>(obj: Type) {return {...obj,on:<PropName extends string & keyof Type>(propName: `${PropName}Changed`) =>(callback: (newValue: Type[PropName]) => void) => {// 在这里可以添加对特定属性的监听逻辑},} as Type & PropEventSource<Type>;
}const person = makeWatchedObject({firstName: "Saoirse",lastName: "Ronan",age: 26,
});person.on("firstNameChanged", (newName) => {console.log(`new name is ${newName.toUpperCase()}`);
});person.on("ageChanged", (newAge) => {});
开胃菜 解析
type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {[P in keyof T as P extends K ? never : P]: T[P];
} & {[P in K]?: T[P];
};interface User {name: string;age: number;email: string;
}
// 示例用法
type PartialUser = PartialByKeys<User, "name" | "age">;const user = {email: "123",
};
我们需要实现 类型指定的键的属性变为可选属性
- 需要接受2个泛型参数 一个是 键值对类型的 一个需要变成可选属性的键名 ,再给 这两个泛型加上限制条件
// T 只能接受 键值 是string 类型的对象 ,K 只能是 T类型的键名
type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {}
- 需要对 T 的 键值 进行遍历处理 先找出不需要处理的键(涉及到过滤不符合条件的键名 需要 配合条件语句 设置为never 来 忽略掉)
// 现在已经将 不需要处理的键值对 取出来
type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {[P in keyof T as P extends K ? never : P]: T[P];
}
- 再使用交互类型 将剩余需要处理成可选的键 一起返回
type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {[P in keyof T as P extends K ? never : P]: T[P];
} & {[P in K]?: T[P];
};
总结
以上基本涵盖了90% ts 的使用。配合类型体操练习题 多练练 ts就算ok了。
下面附上 刷题地址
https://github.com/type-challenges/type-challenges/issues