Skip to content

类型工具

上次更新 2024年10月11日星期五 1:53:42 字数 0 字 时长 0 分钟

is

is 定义变量属于某个类型

下面判断时将出现错误提示

ts
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 类型
ts
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

ts
type T = keyof string;
let a: T = "hello world";

索引类型使用 keyof 时,获取索引名

ts
type T = keyof { name: string; age: number };
let a: T = "name";

下面是获取对象的属性的函数类型定义

ts
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"));

我们也可以使用泛型定义索引类型

ts
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 获取变量类型,下面是获取字符串类型

ts
let hh = "hello world";
type T = typeof hh;
let a: T = "hello world";

下面是使用 typeof 获取对象类型

ts
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 结合定义获取对象属性函数

ts
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 类型索引组成的联合类型
ts
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 实现的类型继承

ts
type A = {
  name: string;
};

interface B extends A {
  age: number;
}

extends 可以用于泛型类型的限定,下面例子中必须包干 id/render 属性,即 T 类型可赋予 extends 右侧类型

ts
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 类型
ts
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 类型

下面是联合类型的条件判断

ts
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>实现

ts
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 实现

ts
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" };

泛型条件分配

如果泛型是普通类型,则与上面一样也是判读那左侧类型是否可以赋予右侧类型

ts
type type1 = string;

type type2<T> = T extends type1 ? string : boolean;

const a: type2<boolean> = true;

如果 extends 是泛型类型,并且传入的类型是联合类型。则分别进行判断,最后得到联合类型。

ts
type type1 = string;

type type2<T> = T extends type1 ? string : boolean;

const a: type2<boolean | string> = "hello";

条件判读也可以嵌套使用

ts
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");

使用 [] 包裹类型,表示泛型的整体进行比较 ❓

ts
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 赋予
ts
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类型了

ts
type type1 = string;

type type2 = string | number;

const a: Exclude<type2, type1> = 12;

Extract

ExtractExclude 相反,用于获取相交的类型。

ts
type EXTRACT<T, U> = T extends U ? T : never;

type type1 = string | number | boolean;

const a: EXTRACT<type1, string | number> = "hello world";

下面是取两个类型相同的属性名

ts
type type1 = string | number | boolean;

const a: Extract<type1, string | number> = "hello";

Pick

pick 可以用于从属性中挑选出一组属性,组成新的类型。

下面定义 pick 类型用于从 type1 类型中挑选出 name 与 age 类型。

ts
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 类型,所以我们不用像上面那样自己定义了

ts
type type1 = { name: string; age: number; skill: string };

type type2 = Pick<type1, "name" | "age">;
const xj: type2 = { name: "hello", age: 33 };

Omit

从类型中过滤掉指定属性,这与 Pick 类型工具功能相反

ts
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 优化

ts
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

ts
type type1 = { name: string; age: number; city: string };

type XJ = Omit<type1, "name" | "age">; //{city:string}

Partial

下面定义 Partial 类型,用于将全部属性设置为可选

ts
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 的支持,所以我们不用自己定义了

ts
type type1 = { name: string; age: number };

const a: Partial<type1> = { name: "hello" };

Record

Record 常用于快速定义对象类型使用

下面我们来手动实现一个 Record,RECORD 类型的第一个参数为索引,第二个为类型

ts
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 类型,索引为字符串,值为任何类型

ts
type HD = Record<"name" | "age", any>;

const xj: HD = { name: "后盾人", age: 18 };

infer

infer 只能在 extends 中使用 infer 的类型变量,只能在 extends 条件的 true 中使用 下面使用 infer 推断属性值类型

ts
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 获取值类型

ts
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>;

下面是获取函数返回值类型

ts
type HD = (n: string) => number[];

type GetFunctionReturnValue<T> = T extends (...args: any) => (infer U)[]
  ? U
  : T;

type valueType = GetFunctionReturnValue<HD>;