类型推断

TS 能根据一些简单的规则推断变量的类型

// 1.从右向左(变量类型可以由定义推断)
let a = 1; // number
let bar = 'zhangsan'; // string
// 2.底部流出(返回值能被 return 语句推断)
function add(a: number, b: number) {
    return a + b;
}
let c = add(1, 3); // number
// 3.从左向右(函数参数类型/返回值类型也能通过赋值来推断)
let fn = (a, b) => a + b;

type Sum = (a: number, b: number) => number;
let sum: Sum = fn; // 返回数字类型

type Sum2 = (a: string, b: string) => string;
let sum2: Sum2 = fn; // 返回字符串类型
// 4.结构化、解构
let person = {
    name: 'zhufeng',
    age: 11
}
let { name, age } = person;
// let name: string
// let age: number
// 5.DefaultProps
interface DefaultProps {
    name?: string;
    age?: number;
}
let defaultProps: DefaultProps = {
    name: '张三',
    age: 10
}

let props = { ...defaultProps, home: '北京' }
type Props = typeof props;
// type Props = {
//     home: string;
//     name?: string;
//     age?: number;
// }
// 6.小心使用返回值
const addOne = (a: any) => a + 1;
let a = addOne(1); // any

const sum3 = (a: number, b: number) => a + addOne(b) as number;
let k = sum3(1, 2); // number

typeof

let obj = { name: '张三', age: 10 }

type P4 = typeof obj;
// type P4 = {
//     name: string;
//     age: number;
// }

索引访问操作符

interface Person {
    name: string;
    age: number;
    job: {
        name: string;
    }
}

let newJob: Person['job'] = { name: '前端' };

映射类型

interface Person {
    name: string;
    age: number;
    gender: 'male' | 'female'
}
type Partial<T> = {
    // 批量把一个接口中的属性全部变成可选的
    [key in keyof T]?: T[key]
}

// 测试
type PPerson = Partial<Person>
// type PPerson = {
//     name?: string;
//     age?: number;
//     gender?: 'male' | 'female';
// }

keyof 操作符

keyof 简介

用于获取某种类型的所有键,其返回类型是联合类型

  1. 用于接口
    interface Person {
      name: string;
      age: number;
      location: string;
    }
    type K1 = keyof Person; // "name" | "age" | "location"
    type K2 = keyof Person[];  // number | "length" | "push" | "concat" | ...
    type K3 = keyof { [x: string]: Person };  // string | number
    
  2. 用于类
    class Person {
      name: string = "Semlinker";
    }
    let sname: keyof Person;
    sname = "name";
    
  3. 用于基本数据类型
    let K1: keyof boolean; // let K1: "valueOf"
    let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
    let K3: keyof symbol; // let K1: "valueOf" | ...
    

keyof 的作用

  1. JavaScript 是一种高度动态的语言,有时在静态类型系统中捕获某些操作的语义可能会很棘手,用 keyof 和泛型能很好的解决问题;
  2. 示例代码:用于获取某个对象中指定属性的属性值,期望用户输入的属性是对象上已存在的属性:
    function prop<T extends object, K extends keyof T>(obj: T, key: K) {
      return obj[key];
    }
    
    // 测试
    type Todo = {
      id: number;
      text: string;
      done: boolean;
    }
    
    const todo: Todo = {
      id: 1,
      text: "Learn TypeScript keyof",
      done: false
    }
    
    const id = prop(todo, "id"); // const id: number
    const text = prop(todo, "text"); // const text: string
    const done = prop(todo, "done"); // const done: boolean
    
    const date = prop(todo, "date"); 
    //当访问 todo 对象上不存在的属性时,会报错
    //类型 "date" 的参数不能赋给类型“keyof Todo”的参数
    

keyof 与 typeof 结合使用

获取对象所有的 key 值的联合类型

const COLORS = {
  red: 'red1',
  blue: 'blue1'
}

// 通过 typeof 操作符获取 COLORS 变量的类型,keyof 操作符获取该类型的所有键
// 即字符串字面量联合类型 'red' | 'blue'(获取 COLORS 所有的 key 值)
type Colors = keyof typeof COLORS
let color: Colors;

color = 'red'    // Ok
color = 'blue'   // Ok
color = 'yellow' // 不能将类型 "yellow" 分配给类型 "red" | "blue"

条件类型

  1. 条件类型
    interface Fish {
      name1: string;
    }
    interface Water {
      name2: string;
    }
    interface Bird {
      name3: string;
    }
    interface Sky {
      name4: string;
    }
    
    // 1.条件类型的分发(必须是裸类型)
    type Condition<T> = T extends Fish ? Water : Sky;
    let con1: Condition<Fish | Bird> = { name2: '' };
    let con2: Condition<Fish | Bird> = { name4: '' };
    
    // 2.条件类型的分发(分发失效,因为 不是裸类型)
    type Condition2<T> = { t: T } extends { t: Fish } ? Water : Sky;
    let con3: Condition2<Fish | Bird> = { name2: '' }; // 不能将类型“{ name2: string; }”分配给类型“Sky”
    let con4: Condition2<Fish | Bird> = { name4: '' };
    
    // 3.找出 T 中不包含 U 的部分
    type Diff<T, U> = T extends U ? never : T;
    type R = Diff<'a' | 'b' | 'c' | 'd', 'a' | 'b' | 'c'>; // never | never | never | 'd' => 'd'
    
    // 4.找出 T 中包含 U 的部分
    type Filter<T, U> = T extends U ? T : never;
    type R3 = Filter<'a' | 'b' | 'c' | 'd', 'a' | 'b' | 'c'>; // 'a' | 'b' | 'c'
    
  2. 内置条件类型
    // 1.找出 T 中不包含 U 的部分
    type Exclude<T, U> = T extends U ? never : T;
    type R4 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'b' | 'c'>;
    
    // 2.找出 T 中包含 U 的部分
    type Extract<T, U> = T extends U ? T : never;
    type R5 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'b' | 'c'>;
    
    // 3.找到非空的部分
    type NonNullable<T> = T extends null | undefined ? never : T;
    type R6 = NonNullable<'a' | 1 | null | undefined>;
    
    // 4.返回值类型
    type ReturnType<T> = T extends (...args: any[]) => infer R ? R : T;
    const getUser = () => ({ name: '张三', age: 10 });
    type ReturnUser = ReturnType<typeof getUser>;
    // type ReturnUser = {
    //     name: string;
    //     age: number;
    // }
    
    // 5.参数类型,infer 为类型推断
    type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
    const getUser = (a: string, b: number) => ({ name: '张三', age: 10 });
    type ParamsType = Parameters<typeof getUser>; // [a: string, b: number]
    
    class Person {
      name: string;
      constructor(name: string) {
        this.name = name;
      }
      getName() { console.log(this.name); }
    }
    
    // 6.获取类的构造函数的参数类型
    type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;
    type Params = ConstructorParameters<typeof Person>;
    
    // 7.获取构造函数类型的实例类型
    type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
    type Person8Instance = InstanceType<typeof Person>;
    
    // 8.将 interface 中的属性全部变成可选属性
    interface Company {
      id: number;
      name: string;
    }
    interface Person {
      id: number;
      name: string;
      company: Company
    }
    // 手动实现可选递归(+? 变成可选)
    type DeepPartial<T> = {
      [U in keyof T]+?: T[U] extends object ? DeepPartial<T[U]> : T[U]
    }
    
    // 测试
    type PartialPerson = DeepPartial<Person>;
    let p: PartialPerson = {
      id: 1,
      name: '张三',
      company: {
          id: 2
      }
    }
    
    // 9.将 interface 中的属性全部变成必选属性
    interface Person {
        name: string;
        age?: number;
    }
    // (-? 变成必选)
    type Required<T> = {
        [P in keyof T]-?: T[P];
    };
    
    // 测试
    type RequiredPerson = Required<Person>;
    let p: RequiredPerson = {
        name: '张三',
        age: 11
    }
    
    // 10.将 interface 中的属性全部变成只读属性
    interface Person {
        name: string;
        age: number;
    }
    type Readonly<T> = {
        readonly [P in keyof T]: T[P];
    };
    
    // 测试
    type ReadOnlyNamePerson = Readonly<Person>;
    let p: ReadOnlyNamePerson = {
        name: '张三',
        age: 11
    }
    p.name = '李四'; // 无法分配到 "name" ,因为它是只读属性
    p.age = 11; // 无法分配到 "age" ,因为它是只读属性
    
    // 11.Pick 从传入属性选取某些属性返回
    interface Person {
        name: string;
        age: number;
        gender: number
    }
    type Pick<T, K extends keyof T> = {
        [P in K]: T[P];
    };
    
    // 测试
    let person: Person = { name: '张三', age: 11, gender: 1 };
    type PickPerson = Pick<Person, 'name' | 'age'>;
    // type PickPerson = {
    //     name: string;
    //     age: number;
    // }
    
    // 12.Extract 找出 T 中包含 U 的部分(条件类型分发)
    type Extract<T, U> = T extends U ? T : never;
    
    // 测试
    type E = Extract<string | number | boolean, string | number>;
    // type E = string | number
    
    // 13.Record 定义一个对象的 key 和 value 类型
    // keyof any => string | number | symbol 联合类型
    type Record<K extends keyof any, T> = {
        [xxx in K]: T;
    };
    
    // 测试,key 是 string | number,value 是 string
    let k: Record<string | number, string> = { name: '张三', age: '11' };
    

自定义高级类型

Proxy 代理

type Proxy<T> = {
  get(): T,
  set(value: T): void;
}
type Proxify<T> = {
  [P in keyof T]: Proxy<T[P]>
}
function proxify<T>(obj: T): Proxify<T> {
  let result = <Proxify<T>>{};
  for (const key in obj) {
    Object.defineProperty(result, key, {
      get: () => {
        return obj[key];
      },
      set: (value) => {
        obj[key] = value;
      }
    });
  }
  return result;
}

// 测试
let proxyProps: any = proxify<{ name: string, age: number }>({ name: '张三', age: 18 });
console.log(proxyProps); // {}
console.log(proxyProps.name); // 张三
proxyProps.name = '李四';
proxyProps.age = 20;
console.log(proxyProps.name); // 李四

SetDifference 差集

// 差集  A-B  = Exclude
type SetDifference<A,B>= A extends B ?never:A;

// 测试
type A = string|number;
type B = number|boolean;
type AB = SetDifference<A,B>;
// type AB = string

Omit 去除指定的属性

type SetDifference<A,B>= A extends B ?never:A;

type Omit<T, K extends keyof any> = Pick<T, SetDifference<keyof T, K>>;

// 测试
type Props = { name: string, age: number, visible: boolean };
type OmitAgeProps = Omit<Props, 'age'>;
// type OmitAgeProps = {
//     name: string;
//     visible: boolean;
// }

Diff 获取两个类型差异的部分

type SetDifference<A, B> = A extends B ? never : A;

type Diff<T extends object, U extends object> = Pick<T, SetDifference<keyof T, keyof U>>;

// 测试
type Props = { name: string, age: number, visible: boolean };
type DefaultProps = { age: number };
type DiffProps = Diff<Props, DefaultProps>;
// type DiffProps = {
//     name: string;
//     visible: boolean;
// }

Intersection 获取两个类型的交集部分

type InterSection<T extends object, U extends object> = 
  Pick<T, Extract<keyof T, keyof U> & Extract<keyof U, keyof T>>;

// 测试
type Props = { name: string, age: number, visible: boolean };
type DefaultProps = { age: number };
type DuplicateProps = InterSection<Props, DefaultProps>;
// type DuplicateProps = {
//     age: number;
// }

Overwrite 覆盖同一属性类型

type Diff<T extends object, U extends object> = Pick<T, SetDifference<keyof T, keyof U>>;

type InterSection<T extends object, U extends object> = 
  Pick<T, Extract<keyof T, keyof U> & Extract<keyof U, keyof T>>;

type Overwrite<T extends object, U extends object, I = Diff<T, U> & InterSection<U, T>> =  
  Pick<I, keyof I>

// 测试
type OldProps = { name: string, age: number, visible: boolean };
type NewProps = { age: string, other: string };
type ReplacedProps = Overwrite<OldProps, NewProps>;
// type ReplacedProps = {
//     name: string;
//     age: string;
//     visible: boolean;
// }

Merge 合并

type SetDifference<A, B> = A extends B ? never : A;
type Omit<T, K extends keyof any> = Pick<T, SetDifference<keyof T, K>>;
// 浅拷贝一个属性类型
type Compute<A extends any> = A extends Function ? A : { [K in keyof A]: A[K] }

type Merge<O1 extends object, O2 extends object> = Compute<O1 & Omit<O2, keyof O1>>

// 测试
type O1 = {
    id: number;
    name: string
}
type O2 = {
    id: number;
    age: number;
}
type R2 = Merge<O1, O2>;
// type R2 = {
//     id: number;
//     name: string;
//     age: number;
// }

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于产品、设计、开发的问题和看法,还有技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程

了解更多

目录

  1. 1. 类型推断
  2. 2. typeof
  3. 3. 索引访问操作符
  4. 4. 映射类型
  5. 5. keyof 操作符
    1. 5.1. keyof 简介
    2. 5.2. keyof 的作用
    3. 5.3. keyof 与 typeof 结合使用
  6. 6. 条件类型
  7. 7. 自定义高级类型
    1. 7.1. Proxy 代理
    2. 7.2. SetDifference 差集
    3. 7.3. Omit 去除指定的属性
    4. 7.4. Diff 获取两个类型差异的部分
    5. 7.5. Intersection 获取两个类型的交集部分
    6. 7.6. Overwrite 覆盖同一属性类型
    7. 7.7. Merge 合并