# TypeScript 挑战

# keyof

keyof用来返回属性的联合类型

type objType = { name: string; age: number };
type keyofType = keyof objType; // 类型为name|age

// 不能直观看到结果,可以用辅助类型来看
type DirectKeys<T> = T extends any ? T : never;
type getKeyofType = DirectKeys<keyofType>; //name | age
type TypeAny = keyof any; // 等同于string|number|symbol,固定结果

# in

in用来迭代联合类型

type K = {
  name: string;
  age: number;
  phone: number;
};

// 给每一个属性添加?
type KType = {
  [P in keyof K]+?: K[P];
  // 等同于
  // name?: K['name'] = string;
  // age?: K['age']=number;
  // phone?: K['phone']=number;
};

# 加号和减号

-: 去掉可选

type Required<T> = {
  [K in keyof T]-?: T[K];
};

type S = {
  a: number;
  b?: number;


type RequiredS = Required<S>; // {a:number,b:number}
type Read<T> = {
  // 非只读
  -readonly [K in keyof T]: T[K];
};

+: 添加可选,可以忽略不写

type Partial<T> = {
  [K in keyof T]+?: T[K];
};

type S = {
  a: number;
  b?: number;
};

type Partial = Partial<S>; // {a?:number,b?:number}

# extends

类型约束,限制 T 只能为联合类型(keyof U)中的一种

T extends keyof U

条件类型 根据符合条件返回

 T extends  U ? T :never

T 如果是联合类型,会逐个进行比较,将最后的结果合并

// 通过T来进行extends,会使里面的类型逐个进行比较,返回的类型会逐个合并
type Tcond<T> = T extends string | number ? T : never;
type TextTcond = Tcond<string | number | boolean>; // string|number

获取两个类型的共同类型

type T1 = "name" | "age" | "sex";
type T2 = "name" | "address";
// 通过迭代去判断是否有交集
type Extract<T, P> = T extends P ? T : never;

type R = Extract<T1, T2>;

# infer

设置占位符,用于使用的时候获取对应的类型

type CarType = (params: Car) => string;

// A extends B,判断A类型是否属于B类型
// 获取Car类型
type getCarP = CarType extends (params: infer P) => string ? P : CarType;
// 获取string类型
type getCarR = CarType extends (params: Car) => infer R ? R : CarType;

# typeof

获取变量的类型

function fn(a: number): number {
  return a;
}

type Tf = typeof fn; // (a:number)=>number

# Pick

从类型 T 中选出符合 K 的属性,构造一个新的类型。

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

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

type TodoPreview = MyPick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

# readonly

Readonly 是用来让所有属性变为只读,其用法为:

interface Todo {
  title: string;
  description: string;
}

type MyReadonly<T> = {
  readonly [P in keyof T]: T[P];
};

const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar",
};
// 给定接口类型中需添加新的类型
interface I1 {
  a: string;
  b: number;
}

type TAddI1<T, K extends string, V> = {
  [P in keyof T | K]: P extends keyof T ? T[P] : V;
};

type TI1 = TAddI1<I1, "c", boolean>;

// 去除key

type Module = {
  a: string | number;
  b: boolean | string;
};
type DeleteModule<T> = {
  [Key in keyof T]: T[Key];
}[keyof T]; // 如果不要key,直接后面加
type GetDeleteModule = DeleteModule<Module>;

// 模版
type template = {
  a: string;
  b: string;
  c: string;
};
type Ttemplate<T, U> = `${T & string} / ${U & string}`;
type template_<T> = {
  [Key in keyof T]: Ttemplate<Key, T[Key]>;
};

type TTtemplate = template_<template>;

// Extract 就是 T extends U ? T : never 的简写,底层实现
type TExtract = Extract<string, string | number>; // string

// Exclude =  T extends U ? never : T
type TestExclude = Exclude<string, string | number>; // never

// Record,比object更加灵活,支持string|number|symbol,对象或者数组
function rec(obj: Record<string, any>) {
  console.log(obj);
}
let recData = rec({ age: "1" });
// 数组也支持"0":1
let recData2 = rec([1, 2]);

// Pick,抓取类型,接口,类中的所需要的属性组成一个对象类型
interface P {
  a: string;
  b: number;
  c: boolean;
}

type SubP = Pick<P, "a" | "b">;

// 映射 in,排除获取
interface P2 {
  a: string;
  b: number;
  c: boolean;
}

type omit<T, K extends keyof T> = {
  [P in keyof T as Exclude<P, K>]: T[P];
};
// 排除c
type getOmit = omit<P2, "c">; // {a:string,b:number}

// Capitalize,首字母转化成大写
type C = "a";
type getC = Capitalize<C>;

// -? 去掉可选
type Required<T> = {
  [K in keyof T]-?: T[K];
};

// ? 添加可选
type Partial<T> = {
  [K in keyof T]+?: T[K];
};

// -readonly 去除只读
type Read<T> = {
  // 只读
  // readonly [K in keyof T]: T[K]
  // 非只读
  -readonly [K in keyof T]: T[K];
};