TS 杂记

136 阅读5分钟

* Ts中 extends 和 implements

*   extends继承,但是不一定需要有对应的属性
*   implements继承,但是一定需要有对应的属性

* as const

  • 定义:常量断言【严格模式,只读属性,子属性也不可修改】,拒绝联合属性,返回类型更准确。在使用as const 后具有如下特征:

    • no literal types in that expression should be widened (e.g. no going from "hello" to string)【不在扩大类型,例如字符串常量"hellow"不会扩大类型到string】
    • object literals get readonly properties 【对象属性变成只读,不可修改】
    • array literals become readonly tuples【数组会变成只读元祖,只读,不可修改】
  • 使用场景:

      //1、定义一个常量,只读,不让修改
      const HEADER_NAME = 'headerSlotForm' as const;
    
      //2、定义一个静态属性常量,只读,不让修改
      class CustomComponentSample {
        public static componentName = 'CustomComponentSample' as const;
      }
    
      //3、A 'const' assertion can only be applied to a string, number, boolean, array, or object literal
      // error!
      let a = (Math.random() < 0.5 ? 0 : 1) as const;
      let b = (60 * 60 * 1000) as const;
      // Works!
      let c = Math.random() < 0.5 ? (0 as const) : (1 as const);
      let d = 3_600_000 as const;
    
      // arr become readonly
      const arr = [1, 2, 3, 4] as const
      // error!
      arr[0]=100;
      arr.push(1000);
    
      //5、Another thing to keep in mind is that const contexts don’t immediately convert an expression to be fully immutable.
      let arr = [1, 2, 3, 4];
      let foo = {
        name: "foo",
        contents: arr,
      } as const;
      // error!
      foo.name = "bar"; 
      foo.contents = []; 
      // works!
      foo.contents.push(5);
      foo.contents[0] = 1000;
    
      //6、This feature means that types that would otherwise be used just to hint immutability to the compiler can often be omitted.
      // Works with no types referenced or declared.
      // We only needed a single const assertion.
      function getShapes() {
        let result = [
          { kind: "circle", radius: 100 },
          { kind: "square", sideLength: 50 },
        ] as const;
        return result;
      }
      for (const shape of getShapes()) {
        // Narrows perfectly!
        if (shape.kind === "circle") {
          // 推导更准确,shape.radius不会报错
          console.log("Circle radius", shape.radius);
        } else {
          console.log("Square side length", shape.sideLength);
        }
      }
    
  • 参考文件:

* Record

  • 定义:Record<Keys, Type>
    • Constructs an object type whose property keys are Keys and whose property values are Type. This utility can be used to map the properties of a type to another type.【定义一个对象,对象的key就是 Record的Keys的类型,对象的value就是 Record的type的类型】
  • 应用场景:
      //1、基础使用
      interface CatInfo {
        age: number;
        breed: string;
      }
      type CatName = "miffy" | "boris" | "mordred";
      // works!!!
      const cats: Record<CatName, CatInfo> = {
        miffy: { age: 10, breed: "Persian" },
        boris: { age: 5, breed: "Maine Coon" },
        mordred: { age: 16, breed: "British Shorthair" },
      };
      cats.boris;
      // error!!! 【缺少mordred属性】
      const cats: Record<CatName, CatInfo> = {
        miffy: { age: 10, breed: "Persian" },
        boris: { age: 5, breed: "Maine Coon" },
      };
    
    
      //2、基础使用
      declare const x: Record<"a", string>;
      x.b; // error, Property 'b' does not exist on type 'Record<"a", string>'
    
      //3、基础使用
      declare function acceptR(x: Record<"a", string>): void;
      acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties
      acceptR({a: "hey", b: "you"}); // okay
    
      //4、基础使用
      type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
      // Is it exactly the same as this?:
      type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
    
  • 参考文献: What is the Record type?

* Pick

  • 定义:Pick<Type, Keys>

    • Constructs a type by picking the set of properties Keys (string literal or union of string literals) from Type.【 通过从 Type 中选择一组属性键(字符串文字或字符串文字的并集)来构造类型 】
  • 应用场景:

      //TS Pick 源码
      type Pick< T, K extends keyof T> = { [P in K] : T[P] }
    
      //基础使用
      interface Todo {
        title: string;
        description: string;
        completed: boolean;
      }
      type TodoPreview = Pick<Todo, "title" | "completed">;
      const todo: TodoPreview = {
        title: "Clean room",
        completed: false,
      };
      todo //TodoPreview
    

* Omit

  • 定义:Omit<Type, Keys> 【必须保证:Keys 是 keyof any(string、number、symbol)三种类型之一】

    • Constructs a type by picking all properties from Type and then removing Keys (string literal or union of string literals). The opposite of Pick.【 通过从 Type 中剔除一组属性键(字符串文字或字符串文字的并集)来构造类型,和pick相反 】
  • 应用场景:

      //官方实现
      type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
      //自定义实现
      type MyOmit<T, K extends keyof any > = { [P in keyof T as P extends K ? never : P]: T[P] };
    
    
      //基础使用
      interface Todo {
        title: string;
        description: string;
        completed: boolean;
        createdAt: number;
      }
      type TodoPreview = Omit<Todo, "description">;
      const todo: TodoPreview = {
        title: "Clean room",
        completed: false,
        createdAt: 1615544252770,
      };
      todo //TodoPreview
    

* new 一个函数怎么定义

  • EX:

      //定义
      Class AAA{
        constructor(param1:any,param2:any)
      }
      //使用
      const instance= new AAA('1',{b:1})
    

* keyof

  • 定义:The keyof operator takes an object type and produces a string or numeric literal union of its keys. keyof操作符 应用于对象类型,返回一个字符串或者数字式的字面量的键的并集

  • JavaScript通过 Object.keys()获取对象的所有属性键值,而typescript主要关注的是类型操作,通过 keyof 操作符可以获取对象中的所有键类型组成的联合类型。

  • 应用场景:

      //基础使用
      type Point = { x: number; y: number };
      type P = keyof Point; //"x" | "y"
    
      //进阶
      type Arrayish = { [n: number]: unknown };
      type A = keyof Arrayish; //number
    
      //进阶
      type Mapish = { [k: string]: boolean };
      type M = keyof Mapish;//【string | number】 --- ***【 Note that in this example, M is string | number — this is because JavaScript object keys are always coerced to a string, so obj[0] is always the same as obj["0"]. 】***
    
      //高阶用法
      //1、获取对象所有属性的类型
      type Person = {
        id: number;
        name: string;
        age: number;
      };
      type P2 = Person[keyof Person];  // number | string
      //解析:type P2 = Person['id' | 'name' | 'age'] = Person['id'] | Person['name'] | Person['age'] = number | string
    
      //2、获取对象所有属性的类型
      type Person = {
        id: number;
        name: string;
        age: number;
      };
      type MyPick<T, K extends keyof T> = { [P in K]: T[P] };
      type P3 = MyPick<Person, 'id' | 'age'>//{ id: number; age: number;}
      //解析:K extends keyof T对K进行了约束,只能是'id','name','age'中的一个类型或者几个类型组成的联合类型;如果没有这个约束,{ [P in K]: T[P] } 则会报错。
    
    
      //3、和条件类型组合实现功能
      //两个对象类型合并成一个新的类型
      type Person = {
        id: number;
        name: string;
        age: number;
      };
      type Skill = {
        run: () => void;
      }
      type Merge<F extends Record<string, any>, S extends Record<string, any>> = {
        [P in keyof F | keyof S]: P extends keyof S ? S[P] : P extends keyof F ? F[P] : never;
      };
      type P7 = Merge<Person, Skill>; // { id: number; name: string; age: number; run: () => void; }
      //解析:案例中P extends keyof S ? X : Y 的部分叫做 条件类型。代码中的含义就是如果 P是F的属性类型,则取F[P],如果P是S的属性类型,则取S[P]。
    
      //4、和映射类型组合实现某些功能
      //给对象类型的所有属性加上readonly修饰符
      type Person = {
        id: number;
        name: string;
        age: number;
      };
      type MyReadonly<T> = { readonly [P in keyof T]: T[P] };
      type P4 = MyReadonly<Person>;  // { readonly id: number; readonly name: string; readonly age: number; }
      //解析:T[P]是查询类型,上面介绍过了,Person['id'] 的结果是number,Person['name'] 的结果是string,Person['age'] 的结果是number。将每个属性类型添加readonly修饰符,最后的结果就是 { readonly id: number; readonly name: string; readonly age: number; }
    
      //5、和映射类型组合实现某些功能
      //去掉对象类型的某些属性【微软官是通过Pick 和exclude组合来实现Omit逻辑的,我们可以通过以下的代码实现同样的功能。】
      type Person = {
        id: number;
        name: string;
        age: number;
      };
      type MyOmit<T, K> = { [P in keyof T as P extends K ? never : P]: T[P] };
      type P5 = MyOmit<Person, 'id' | 'name'> // {age: number;}
      //解析:代码中的as P extends K ? never : P这部分代码叫做重映射 ,因为我们不一定需要的是P,有些情况下需要对P进行一些转换;案例中K 中包含的P键值类型则通过never忽略了,相反则保留。所以最后的结果是{age: number;}
    
      //6、和映射类型组合实现某些功能
      //给对象类型添加新的属性
      type Person = {
        id: number;
        name: string;
        age: number;
      };
      type AppendToObject<T, U extends keyof any, V> = {[P in keyof T | U]: P extends keyof T ? T[P] : V}
      type P6 = AppendToObject<Person, 'address', string> // { address: string; id: number; name: string; age: number; }
      //解析:代码中的U extends keyof any,为新的键,P extends keyof T 条件类型,如果P是Person的键值类型,则取 T[P],否则则取V
      
      //7、keyof any
      type KEY =  keyof any //即 string | number | symbol
    

* never 类型判断时会被忽略

  • EX:
      type T = P extends K ? never : P //T的类型可能是undefined