类型工具
is
is 定义变量属于某个类型
下面判断时将出现错误提示
const isString = (x: unknown): boolean => typeof x === "string";
function hh(a: unknown) {
if (isString(a)) {
console.log(a);
// error:a对象的类型为"unknown"
a.toUpperCase();
}
}
let a = "abcd";
hh(a);
下面使用 is 来定义变量为某个类型。
- x is string 表示如果函数返回值为 true,则 x 为 string 类型
const isString = (x: unknown): x is string => typeof x === "string";
function hh(a: unknown) {
if (isString(a)) {
console.log(a);
a.toUpperCase();
}
}
let a = "abcd";
hh(a);
keyof
获取类、接口索引组成的联合类型
- keyof 可用基本数据类型、any、class、interface、enum 等
任何类型都可以使用 keyof
type T = keyof string;
let a: T = "hello world";
索引类型使用 keyof 时,获取索引名
type T = keyof { name: string; age: number };
let a: T = "name";
下面是获取对象的属性的函数类型定义
function getAttribute<T>(obj: T, key: keyof T): T[keyof T] {
return obj[key];
}
const obj = { name: "hello", age: 18 };
console.log(getAttribute(obj, "name"));
我们也可以使用泛型定义索引类型
function getAttribute<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const obj = { name: "hello", age: 18 };
console.log(getAttribute(obj, "name"));
// 按道理应该会自动推导的
typeof
使用 typeof 获取变量类型,下面是获取字符串类型
let hh = "hello world";
type T = typeof hh;
let a: T = "hello world";
下面是使用 typeof 获取对象类型
let obj = { name: "hello", age: 18 };
// 约束(key字段)类型
// 等同于 type T = { name: string; age: number }
type T = typeof obj;
let a: T = { name: "hello1", age: 12 };
下面是 typeof 与 keyof 结合定义获取对象属性函数
function getAttribute(obj: object, key: string) {
return obj[key as keyof typeof obj];
}
// typof obj ===> {name:string,age:number}
// keyof obj ===> name|age
const a = { name: "hello1", age: 12 };
getAttribute(a, "name");
in
用于 遍历接口 或联合类型的属性
- K in keyof T 指 K 类型为 keyof T 获取的 T 类型索引组成的联合类型
type USER = { name: string; age: number };
type MEMBER<T> = {
[K in keyof T]: T[K];
};
const hd: MEMBER<USER & { address: string }> = {
age: 10,
name: "张三",
address: "天津",
};
extends
extends
在 TS 中拥有多个特性,下面我们分别介绍:
类型继承
extends 实现的类型继承
type A = {
name: string;
};
interface B extends A {
age: number;
}
extends 可以用于泛型类型的限定,下面例子中必须包干 id/render 属性,即 T 类型可赋予 extends 右侧类型
function getInfo<T extends { id: number; render: (n: number) => number }>(
arr: T[]
) {
arr.map((a) => a.render(a.id));
}
getInfo([{ id: 12, render: (n) => console.log(n) }]);
类型条件判断
extends 用于条件判断来决定返回什么类型,A extends B ? true:false
。如果 A(狭窄类型)可以赋予 B(宽泛类型),则返回 true,否则返回 false。
- 下面的例子变量 a 必须为 false,因为 type1 不包含 type2 类型
type type1 = { name: string; age: number };
type type2 = { name: string; age: number; address: string };
type a = type1 extends type2 ? true : false;
const a: a = false; // 只能赋予false 因为 type1 不包含 type2 类型
下面是联合类型的条件判断
type type1 = string;
type type2 = string | number;
type a = type1 extends type2 ? boolean : string;
type b = type2 extends type1 ? boolean : string;
const a: a = true; // 只能赋予boolean 因为 type1 包含 type2 类型
const b: b = "hello"; // 只能赋予string 因为 type2 不包含 type1 类型
根据联合类型过滤掉指定索引 使用 Exclude<K, U>
实现
type User = { name: string; age: number; get(): void };
type Filter<T, U> = {
[K in keyof T as Exclude<K, U>]: T[K];
// 语法层面解释:K in keyof T as Exclude<K, U> 遍历泛型T的key,
// 如果K不是U,则返回K,如果K是U,则name|age|get as get 返回 get
};
type a = Filter<User, "name" | "age">; // {get():void}
const a: a = { get: () => {} };
过滤掉指定类型,以下代码含有下面几种意思
- 根据类型获取组合类型的联合类型
- 根据新的联合类型提取指定的索引,组合新的的类型
使用 Pick
实现
type User = { name: string; age: number; get(): void };
type Filter<T, U> = {
[K in keyof T]: T[K] extends U ? never : K;
}[keyof T];
type a = Pick<User, Filter<User, Function | number>>; //
const a: a = { name: "hello world" };
泛型条件分配
如果泛型是普通类型,则与上面一样也是判读那左侧类型是否可以赋予右侧类型
type type1 = string;
type type2<T> = T extends type1 ? string : boolean;
const a: type2<boolean> = true;
如果 extends 是泛型类型,并且传入的类型是联合类型。则分别进行判断,最后得到联合类型。
type type1 = string;
type type2<T> = T extends type1 ? string : boolean;
const a: type2<boolean | string> = "hello";
条件判读也可以嵌套使用
type type1 = string;
type type2 = string | number;
type type3<T> = T extends type1 ? string : T extends type2 ? symbol : boolean;
const hd: type3<number> = Symbol("key");
使用 [] 包裹类型,表示泛型的整体进行比较 ❓
type type1 = string;
type type2 = string | number;
type type3<T> = T extends type1 ? string : T extends type2 ? symbol : boolean;
const hd: type3<number> = Symbol("key");
Exclude
我们利用泛型类型的条件判断,可以创建一个类型用于进行类型的过滤
。
- 从 T 泛型类型 中过滤 U
- never 是任何类型的子类型,可以被任何类型赋予,除了 never 类型,没有类型可以被 never 赋予
- ❓
type EXCLUDE<T, U> = T extends U ? never : T;
type type1 = string;
type type2 = string | number;
const a: EXCLUDE<type2, type1> = 12;
const b: type1 extends type2 ? number : string = 12;
事实上 typescript 已经提供了 Exclude
关键字 用于完成上面工作,所以我们不需要单独定义 Exclude
类型了
type type1 = string;
type type2 = string | number;
const a: Exclude<type2, type1> = 12;
Extract
Extract
与 Exclude
相反,用于获取相交
的类型。
type EXTRACT<T, U> = T extends U ? T : never;
type type1 = string | number | boolean;
const a: EXTRACT<type1, string | number> = "hello world";
下面是取两个类型相同的属性名
type type1 = string | number | boolean;
const a: Extract<type1, string | number> = "hello";
Pick
pick 可以用于从属性中挑选出一组属性,组成新的类型。
下面定义 pick 类型用于从 type1 类型中挑选出 name 与 age 类型。
type type1 = { name: string; age: number; skill: string };
type PICK<T, U extends keyof T> = {
[P in U]: T[P];
};
type type2 = PICK<type1, "name" | "age">;
const xj: type2 = { name: "hello", age: 33 };
同样 typescript 已经原生提供了 Pick 类型,所以我们不用像上面那样自己定义了
type type1 = { name: string; age: number; skill: string };
type type2 = Pick<type1, "name" | "age">;
const xj: type2 = { name: "hello", age: 33 };
Omit
从类型中过滤
掉指定属性,这与 Pick 类型工具功能相反
type type1 = { name: string; age: number; city: string };
type MyOmit<T, U> = Pick<
T,
{
[K in keyof T]: K extends U ? never : K;
}[keyof T]
>;
type XJ = MyOmit<type1, "name" | "age">; //{city:string}
可以将上面代码使用 Exclude 优化
type type1 = { name: string; age: number; city: string };
type MyOmit<T, U> = Pick<T, Exclude<keyof T, U>>;
type XJ = MyOmit<type1, "name" | "age">; //{city:string}
typescript 已经提供了类型工具 Omit
type type1 = { name: string; age: number; city: string };
type XJ = Omit<type1, "name" | "age">; //{city:string}
Partial
下面定义 Partial 类型,用于将全部属性设置为可选
type type1 = { name: string; age: number };
type PARTIAL<T> = {
[P in keyof T]?: T[P];
};
const a: PARTIAL<type1> = { name: "hello" }; // {name?:string,age?:number}
Typescript 原生提供了 Partial 的支持,所以我们不用自己定义了
type type1 = { name: string; age: number };
const a: Partial<type1> = { name: "hello" };
Record
Record 常用于快速定义对象类型使用
下面我们来手动实现一个 Record,RECORD 类型的第一个参数为索引,第二个为类型
type RECORD<K extends string | number | symbol, V> = {
[P in K]: V;
};
type HD = RECORD<"name" | "age", string | number>;
const xj: HD = { name: "后盾人", age: 18 };
typescript 原生已经提供了 Record 类型,下面定义 MEMBER 类型,索引为字符串,值为任何类型
type HD = Record<"name" | "age", any>;
const xj: HD = { name: "后盾人", age: 18 };
infer
infer 只能在 extends 中使用 infer 的类型变量,只能在 extends 条件的 true 中使用 下面使用 infer 推断属性值类型
type HD = { name: string; age: number };
type AttrType<T> = T extends { name: infer M; age: infer M } ? M : T;
type valueType = AttrType<HD>; //string | number
下面使用 infer 获取值类型
type USER = { name: string; age: number; get(a: string): void };
type GetType<T> = {
[K in keyof T]: T[K] extends infer U ? U : K;
}[keyof T];
type valueType = GetType<USER>;
下面是获取函数返回值类型
type HD = (n: string) => number[];
type GetFunctionReturnValue<T> = T extends (...args: any) => (infer U)[]
? U
: T;
type valueType = GetFunctionReturnValue<HD>;