学习 TypeScript | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 4 天

0x1 基本语法

  1. 什么是 TypeScript

    TypeScript 是基于 JavaScript 发展而来,是静态类型的类型语言

    JS-动态类型:编译在执行阶段进行

    TS-静态类型:编译在执行之前进行

    TypeScript 特点
    静态类型
    JS 的超集
    可读性增强
    基于语法解析 TSDoc 和 IDE 的增强
    可维护性增强
    在编译阶段暴露大部分错误
    在多人合作的大型项目中获得更好的稳定性和开发效率
    包含于兼容所有 JS 特性, 支持共存
    支持渐进式引入于升级
  2. 基本语法

    1. 基础数据类型

      const str: string = 'a string';
      const num: number = 10;
      const boo: boolean = true;
      const nul: null = null;
      const und: undefined = undefined;
      
    2. 对象数据类型

      const obj2: obj1 = {
          id: 001,
          name: 'xxx',
          age: 20
      }
      interface obj1 {
          readonly id: number;	// 只读属性
          name: string;
          age: number;
          other?: string;			// 可选属性
          [key: string]: any;		 // 任意属性
      }
      /* 错误1:只读属性值不可更改 */
      obj2.id = 002;
      /* 错误2:除可选和任意属性,其他属性的值不可缺省 */
      const obj3: obj1 = {
          id: 003,
          age: 30,
          other: 'yyy'
      }
      /* 任意属性实际操作 */
      obj3.newString = 'zzz';
      
    3. 函数类型

      1. 类型声明

        function add(x: number, y: number): number {
            return x + y;
        }
        
        const sub: (x: number, y: number) => number = (x, y) => x - y;
        
        interface Mul {
            (x: number, y: number): number;
        }
        const mul: Mul = (x, y) => x * y;
        
      2. 函数重载

        function func(type: 'string', y?: string): string;
        interface Func {
            (type: 'string', y?: string): string;
            (type: 'date', y?: string): Date;
            (: 'string' | 'date', y?: string): Date | string;
        }
        
    4. 数组类型

      /* 类型+方括号 */
      type Arr1 = number[];
      const arr1: Arr1 = [1, 2, 3, 4, 5, 6];
      /* 泛型表示 */
      type Arr2 = Array<string | number | Record<string, number>>;
      const arr2: Arr2 = [1, 2, '3', '4', { a: 1 }];
      /* 元祖表示 */
      type Arr3 = [number, number, string, string];
      const arr3: Arr3 = [1, 2, '3', '4'];
      /* 接口表示 */
      interface Arr4 {
          [key: number]: any;
      }
      const arr4: Arr4 = ['string', () => null, {}, []];
      
    5. TypeScript 补充类型

      /* 空类型:表示无赋值 */
      type EmptyFunction = () => void;
      
      /* 任意类型:是所有类型的子类型 */
      type AnyType = any;
      
      /* 枚举类型 */
      enum Alpha { A, B, C };
      Alpha['A'] === 0;
      Alpha[0] === 'A';
      
      enum EnumExample {
          add = '+',
          mult = '*'
      }
      EnumExample['add'] === '+';
      EnumExample['+'] === 'add';
      
      /* 泛型 */
      type NumArr = Array<number>;
      
    6. 泛型

      function getRepeatArr(target) {
          return new Array(100).fill(target);
      }
      type IGetRepeatArr = (target: any) => any[];
      
      /* 不预先指定具体类型,而是在使用的时候再指定类型的一种特性 */
      type IGetRepeatArrTemplate = <T>(target: T) => T[];
      
      /* 泛型接口 & 多泛型 */
      interface IX<T, U> {
          key: T;
          val: U;
      }
      
      /* 泛型类 */
      class IMan<T> {
          instance: T;
      }
      
      /* 泛型别名 */
      type ITypeArr<T> = Array<T>
      
      /* 泛型约束:限制泛型必须符合字符串 */
      type IGetRepeatStringArr = <T extend string>(target: T) => T[];
      
      /* 泛型参数默认类型 */
      type IGetRepeatArr<T = number> = (target: T) => T[];
      
    7. 类型别名和类型断言

      /* 通过 type 关键字定义了 IObjArr 的别名类型 */
      type IObjArr = Array<{
          key: string;
          [objKey: string]: any;
      }>
      function keyBy<T extends IObjArr>(objArr: Array<T>) {
      	/* 未指定类型时,result 的类型为 {} */
          const result = objArr.reduce((res, val, key) => {
              res[key] = val;
              return res;
          }, {});
      	/* 通过 as 关键字,断言 result 类型为正确类型 */
          reutnr result as Record<string, T>;
      }
      
    8. 字符串/数字 字面量

      /* 允许指定字符串/数字必须的固定值 */
      
      /* IDomTag 必须为 html、body、div、span 中的其一 */
      type IDomTag = 'html' | 'body' | 'div' | 'span';
      /* IOddNumber 必须为 1、3、5、7、9 中的其一 */
      type IOddNumber = 1 | 3 | 5 | 7 | 9;
      

0x2 高级数据类型

  1. 联合/交叉类型

    举例:对图书进行分类

    const bookList = [{
        author: 'aaa',
        type: 'history',
        range: '1978-2018'
    }, {
        author: 'bbb',
        type: 'story',
        theme: 'love'
    }]
    
    • 联合类型:A | B,表示一个值可以是几种类型之一
    • 交叉类型:A & B,多种类型叠加到一起成为一种类型,包含了所需的所有类型的特性
    type IBookList = Array<{
        author: string;
    } & ({
        type: 'history';
        range: string;
    } | {
        type: 'story';
        theme: string;
    })>
    
  2. 类型保护与类型守卫

    interface IA { a: 1, aa: 2 };
    interface IB { b: 1, bb: 2 };
    
    /* 类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域 */
    function getIsIA(arg: IA | IB): arg is IA {
        return !!(arg as IA).a;
    }
    
    /*
    	在 JS 中不会报错
    	在 TS 中属于错误:
    		类型 IA | IB 上不存在属性 a;类型 IB 上不存在属性 a
    	上述情况说明:访问联合类型时,处于程序安全的原因,仅能访问联合类型中的交集部分
    */
    function opt1(arg: IA | IB) {
        if(arg.a) {
            console.log(arg.aa);
        } else {
            console.log(arg.bb);
        }
    }
    /* 正确写法 */
    function opt2(arg: IA | IB) {
        if(getIsIA(arg)) {
            console.log(arg.aa);
        } else {
            console.log(arg.bb);
        }
    }
    

    结合上述的 “ 图书分类 ” 案例,使用类型保护:

    function optBook(book: IBookList) {
        // 联合类型 + 类型保护 = 自动类型推断
        if(book.type === 'history') {
            console.log(book.range);
        } else {
            console.log(book.theme);
        }
    }
    
  3. merge()函数类型

    /* 要求 sourceObj 必须为 targetObj 的子集 */
    function merge1(sourceObj, targetObj) {
        const result = { ...sourceObj };
        for(let key in targetObj) {
            const itemVal = sourceObj[key];
            itemVal && ( result[key] = itemVal )l
        }
        return result;
    }
    function merge2(sourceObj, targetObj) {
        return { ...sourceObj, ...targetObj };
    }
    
    /* 使用泛型编写类型 */
    interface IMerge {
        <T extends Record<string, any>>(sourceObj: Partial<T>, targetObj: T): T;
    }
    type IPartial<T extends Record<string, any>> = {
        [P in keyof T]?: T[P];
    }
    type IKeys = keyof { a: string, b: number };
    

    索引类型

    • 关键字keyof相当于取值对象中的所有 key 组成的字符串字面量 (Ikeys 相当于type IKeys = "a" | "b";
    • 关键字in相当于取值字符串字面量中的一种可能,配合泛型 P,即表示每个 key
    • 关键字?通过设定对象可选选项,即可自动推导出子集类型
  4. 函数返回值类型

    举例:实现函数 delayCall 的类型声明,该函数接收一个函数入参,实现延迟 1000ms 运行函数,返回 Promise,结果为入参函数的返回结果

    1. 函数方法

      function delayCall(func) {
          return new Promise(resolve => {
              setTimeout(() => {
                  const result = func();
                  resolve(result);
              }, 1000);
          });
      }
      
    2. 泛型方法

      type IDelayCall = <T extends () => any>(func: T) => ReturnType<T>;
      type IReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R :any;
      
      • 关键字extends跟随泛型出现时,表示类型推断,其表达式可类比三元表达式 (如:T === 类型判断 ? 类型A : 类型B

      • 关键字infer出现在类型推荐中,表示定义类型变量,可以用于指代类型

        (如:上述代码将函数的返回值类型作为变量,使用新泛型R表示,使用在类型推荐中的结果中)

0x3 工程应用

  1. 浏览器 Web

    以 webpack 举例:在工程中使用 webpack 执行 TypeScript

    1. 配置 webpack loader 相关配置(loader 用于将文件转化为 webpack 可以识别的文件)
      • awesome-typescript-loader:npm install awesome-typescript-loader --save-dev
      • babel-loader:npm install -D babel-loader @babel/core @babel/preset-env webpack
    2. 配置 tsconfig.js 文件
    3. 运行 webpack 启动/打包
    4. loader 处理 TypeScript 文件时,会进行编译和类型检查
  2. NodeJS

    使用 TSC 编译

    copde.ts
    TSC
    code.js
    1. 安装 Node 与 npm
    2. 配置 tsconfig.js 文件
    3. 使用 npm 安装 TSC
    4. 使用 tsc 运行编译得到对应的 js 文件
posted @ 2023-01-21 16:43  SRIGT  阅读(5)  评论(0编辑  收藏  举报  来源