TypeScript工具类型

2023-11-09  本文已影响0人  Jc_wo

类型挑战【转载】:https://wangtunan.github.io/blog/typescript/challenge.html#%E4%BB%8B%E7%BB%8D

工具类型

Partial<Type>(可选)

映射修饰符可以以两种方式影响可选性,可以通过前缀 - 或者 + (即-?:/+?:)删除或者添加这些修饰符,如果没有写前缀,相当于使用了 + 前缀

Required<Type>(必选)

Readonly<Type>(只读)

Record<Keys, Type>

Pick<Type, Keys>(挑选)

Omit<Type, Keys>(省略)

Exclude<T, U> (排除)

type T0 = Exclude<"a" | "b" | "c", "a">;
/**
* => type T0 = "b" | "c"
*/

Extract<T, U>(提取)

NonNullable<Type>(非空)

Parameters<Type>(参数的类型)

扩展infer

ReturnType<Type>(返回值类型)

ConstructorParameters<Type>(获取构造函数参数类型)

扩展1:为什么要对T约束为抽象类(abstract

abstract用来定义抽象类以及抽象类中的抽象方法

// 普通类
class Test {}
// 抽象类
abstract class TestAbst {}

const T1: typeof Test = Test      // 可以赋值
const T2: typeof Test = TestAbst  // Error: 无法将 抽象构造函数类型 分配给 非抽象构造函数类型。

const TAbs1: typeof TestAbst = Test      // 可以赋值
const TAbs2: typeof TestAbst = TestAbst  // 可以赋值

扩展2:关于类类型的表示

如上const T1: typeof Test = Test,虽然用typeof表示类的类型非常方便(其实它不是用来干这个的, 至少不全是),但不能因每次要写一个类类型时都先用class关键词定义一个类。

替代方法:使用构造函数表示class的类型

type PClass = new (name: string, age: number) => { name: string, age: number };

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

const TestPerson: PClass = Person

也可以在接口interface中使用:

interface PInter {
  new (name: string, age: number): { name: string, age: number }
}

const TestPerson2: PInter = Person

InstanceType<Type>(获取构造函数返回值的类型)

InstanceType用于获取类的实例化类型,即 new 类 的产物,可以说是执行构造函数的返回值,与上面的ConstructorParameters类型相对应

ThisParameterType<Type>(函数参数this的类型)

OmitThisParameter<Type>

ThisType<Type>(上下文this类型的标记)

此实用程序不返回转换后的类型。相反,它充当上下文类型的标记。(Tips:必须启用noImplicitThis标志才能使用此实用程序)

内部字符串操作类型

type Eg1 = Uppercase<'abcd'>;     // type Eg1 = "ABCD"

type Eg2 = Lowercase<'ABCD'>;     // type Eg2 = "abcd"

type Eg3 = Capitalize<'Abcd'>;    // type Eg3 = "Abcd"

type Eg4 = Uncapitalize<'aBCD'>;  // type Eg4 = "aBCD"

扩展:自定义工具类型

1.获取不同时存在于 TU 内的类型

实现:type Exclusive<T, U> = Exclude<T | U, T & U>;

// 实现
type Exclusive<T, U> = Exclude<T | U, T & U>;

// 示例
type Eg = Exclusive<"a" | "b" | "c", "Er" | "a" | "b">   // type Eg = "c" | "Er"

2.获取T中所有类型为函数的key组成的联合类型

type FunctionKeys<T> = {
  [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];

interface Test {
  foo(): void;
  bar: string;
  baz: () => number;
}

type TestFunction = FunctionKeys<Test>; // "foo" | "baz"

3.获取对象中指定类型的字段(由2改)

根据指定类型U,在对象T中挑选出所有类型一致的字段并组成一个新的类型。

type TypeKeys<T, U> = {
  [K in keyof T]: T[K] extends U ? K : never;
}[keyof T];

type PickByType<T, U> = Pick<T, TypeKeys<T, U>>;

interface Example {
  foo: string;
  bar: number;
  baz: string;
}

type StringFieldsOfExample = PickByType<Example, string>; // { foo: string, baz: string }
type NumberFieldsOfExample = PickByType<Example, number>; // { bar: number; }

4.从数组中提取指定属性的值(由2/3改)

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

function pluck<T, K extends keyof T>(arr: T[], key: K): PickKeys<T, K>[] {
  return arr.map((item) => item[key]);
}

interface Example3 {
  foo: string;
  bar: number;
}
const exampleList: Example3[] = [
  { foo: 'hello', bar: 1 },
  { foo: 'world', bar: 2 },
];

const fooList = pluck(exampleList, 'foo'); 
console.log('fooList', fooList);    // ["hello", "world"]

通过PickKeys从对象中提取指定属性的值。定义pluck函数,用于从数组中提取指定属性的值,并返回一个新的数组

5.查找T所有非只读类型的key组成的联合类型

/**
 * 核心实现
 */
type MutableKeys<T extends object> = {
  [P in keyof T]-?: IfEquals<
    { [Q in P]: T[P] },
    { -readonly [Q in P]: T[P] },
    P
  >;
}[keyof T];

/**
 * @desc 一个辅助类型,判断X和Y是否类型相同,
 * @returns 是则返回A,否则返回B
 */
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
  ? A
  : B;

示例:MutableKeys只适用一维对象,无法对嵌套对象生效

/**
 * 示例
 */
interface Person {
  readonly name: string;
  age: number;
  address: {
    readonly street: string;
    city: string;
  };
}


type T0 = MutableKeys<Person>   // type T0 = "age" | "address"

// 测试
type Test = Pick<Person, MutableKeys<Person>>
/**
 * =>
 * type Test = {
 *  age: number;    
 *  address: {       
 *    readonly street: string;
 *    city: string;
 *  };
 * }
 */
const person: Person = {
  name: "Alice",
  age: 30,
  address: {
    street: "123 Main St",
    city: "Springfield",
  },
};
const P2: Test = {
  age: 32,
  address: {
    street: "123 Main St",
    city: "Springfield",
  },
};

6.获取T中所有的可选项或key

type OptionalKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];

使用映射遍历所有Key,通过Pick<T, K>提取当前Key和类型;

利用{} extends {当前key: 类型}判断是否为可选类型。

// Eg2 = false
type Eg2 = {} extends {key1: string} ? true : false;
// Eg3 = true
type Eg3 = {} extends {key1?: string} ? true : false;

利用的就是{}和只包含可选参数类型{key?: string}是兼容的这一特性。把extends前面的{}替换成object也是可以的。

/**
 * 测试例
 */
interface E1 {
  a: string;
  b?: number;
  c?: boolean;
  c2: string;
}
/**
 * T 中所有可选项的key的联合类型
 * type K1 = "b" | "c"
 */
type K1 = OptionalKeys<E1>;

/**
 * T 中所有可选项
 * type T1 = {
 *    b?: number | undefined;
 *    c?: boolean | undefined;
 * }
 */
type T1 = Pick<E1, K1>

7.Pick扩展:获取T中指定类型的所有项

/**
 * 辅助函数
 * 用于获取 T 中类型不为 never 的类型组成的联合类型
 */
type TypeKeys<T> = T[keyof T];

/**
 * 核心实现
 * undefined 的存在 可以获取可选项的值
 */
type PickByValue<T, V> = Pick<T,
  TypeKeys<{ [P in keyof T]: T[P] extends V | undefined ? P : never }>
>;


// 示例
interface E1 {
  a: string;
  a2?: string;
  b: number;
  b1: number | string;
  b2?: number;
}

type T1 = PickByValue<E1, string>
/**
 * type T1 = {
 *   a: string;
 *   a2?: string | undefined;
 * }
 * ----------------------------------------
 * 注意: 如果将 T[P] extends V | undefined 中的 undefined 去掉,则拿到的值为
 *  type T1 = {
 *    a: string;
 *  }
 */

如果 T 中属性的值可能是多个类型并且这些类型之间存在兼容性关系(比如 number | string),需要使用 Extract 类型来进行类型匹配。

type TypeKeys<T> = T[keyof T];

type PickByValueExact<T, V> = Pick<T,
  TypeKeys<{[P in keyof T]: Extract<T[P], V> extends never ? never : P}>
>;


interface E1 {
  a: string;
  a1: string | boolean;
  b: number;
  b1: number | string;
  c?: boolean;
}
type T1 = PickByValueExact<E1, boolean>
/**
 *  type T1 = {
 *    a1: string | boolean;
 *    c?: boolean | undefined;
 *  }
 */

8.删除T中指定类型的所有项(由7改)

type TypeKeys<T> = T[keyof T];

type OmitByValueExact<T, V> = Omit<T,
  TypeKeys<{[P in keyof T]: Extract<T[P], V> extends never ? never : P}>
>;
/**
 * 等同于(Pick写法) =>
 * type OmitByValueExact<T, V> = Pick<T,
 *  TypeKeys<{ [P in keyof T]: Extract<T[P], V> extends never ? P : never }>
 * >;
 */

// 示例
interface E1 {
  a: string;
  a1: string | boolean;
  b: number;
  b1: number | string;
  c?: boolean;
}
type T1 = OmitByValueExact<E1, boolean>
/**
 *  type T1 = {
 *    a: string;
 *    b: number;
 *    b1: number | string;
 *  }
 */

9.Pick扩展:获取 TU共同存在key和对应类型

// T extends object, U extends object
type Intersection<T, U> = Pick<T, Extract<keyof U, keyof T> & Extract<keyof T, keyof U>>

// 使用
interface E1 {
  a: string;
  a1: string | boolean;
  b: number;
  b2?: number;
}

interface E2 {
  a: string;
  a1: boolean;
  b2?: number;
}
type T1 = Intersection<E1, E2>
// type T1 = {
//   a: string;
//   a1: string | boolean;
//   b2?: number | undefined;
// }

2次Extract的原因是为了避免类型的兼容推导问题。

10.去除T中存在于Ukey和对应类型(由9改)

type Diff<T, U> = Pick<T, Exclude<keyof T, keyof U>>

// 使用
interface E1 {
  a: string;
  a1: string | boolean;
  b: number;
  b2?: number;
}

interface E2 {
  a: string;
  a1: boolean;
  b2?: number;
  c: string
}

type T1 = Diff<E1, E2>
// type T1 = {
//   b: number;
// }

11.OverwriteAssign(9/10改)

// 合并,相同key和对应类型由后者覆盖前者
type Assign<T, U, I = Diff<T, U> & U> = Pick<I, keyof I>;

// 示例
interface E1 {
  a: string;
  b: string | boolean;
}

interface E2 {
  a: string;
  c: string
}

type Eg2 = Assign<E1, E2>
// type Eg2 = {
//   a: string;
//   b: string | boolean;
//   c: string;
// }
// 获取前者独有的key和类型,再取两者共有的key和该key在后者中的类型,最后合并。
// T extends object, U extends object
type Overwrite<T, U, I = Diff<T, U> & Intersection<U, T>> = Pick<I, keyof I>

interface E1 {
  a: string;
  b3: number;
}

interface E2 {
  a: string;
  a1: boolean;
  b2: number;
  c: string
}
type Eg1 = Overwrite<E1, E2>
// type Eg1 = {
//   a: string;
//   b3: number;
// }

12.将联合类型转变成交叉类型

type UnionToIntersection<T> = (
  T extends any ? (arg: T) => void : never
) extends (arg: infer U) => void ? U : never;


interface A {
  a: string;
}

interface B {
  b: number;
}

type C = UnionToIntersection<A | B>; 
// { a: string } & { b: number }
上一篇 下一篇

猜你喜欢

热点阅读