TypeScript入门| 青训营笔记

81 阅读5分钟

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

这篇笔记记录一些TypeScript的基本语法和知识点。

一、本堂课重点内容

  • TypeScript 基本语法
  • TypeScript 高级数据类型

二、详细知识点介绍

1、TypeScript特性

  • TypeScript是一门弱类型、静态的语言
    • 弱类型语言:允许隐式类型转换
    • 静态:编译时就确定变量的数据类型。动态语言是运行时确定
    • 静态语言特性
      • 可读性强
      • 可维护性强:编译时发现错误
    • TS是JS的超集

2、基本语法

  • 基础数据类型:stringnumberbooleannullundefined
  • 对象类型
    • readonly:只读属性
    • ?.:可选属性
    • [key: string]: any:表示这一项可有可无,且必须key为字符串类型,value为任意类型
interface IByteDance {
    readonly jobId: number;
    name: string;
    sex: 'max' | 'woman' | 'other';
    age: number;
    hobby?: string;
    [key: string]: any;
}
  • 函数类型
interface IMult {
    (x: number, y: number): number;
}
const mult: IMult = (x, y) => x * y;
/* ---或者--- */
const mult: (x: number, y: number) => number = (x, y) => x * y;
  • 函数重载
function getDate(type: string, timestamp?: string): string;
interface IGetDate {
    (type: string, timestamp?: string): string;
    (type: Date, timestamp?: string): Date;
    (type: string | Date, timestamp?: string): Date | string;
const getDate2: IGetDate = (type: string, timestamp: string) => {
    const date = new Date(timestamp);
    return type === string ? date | date.toLocaleString();
}
}
  • 数组类型
    • 类型 + 方括号:const arr1: number[] = [1, 2, 3];
    • 泛型
    type IArr2 = Array<string | number | Record<string, number>>
    const arr2: IArr2 = [1, '2', {a: 1}]
    
    • 元组
    type IArr3 = [number, string, string];
    const arr3: IArr3 = [1, '2', '3'];
    
    • 接口
    interface IArr4 {
        [key: number]: any;
    }
    const arr4: IArr4 = [...];
    
  • 补充类型
    • void
    • any
    • 枚举
    // 定义枚举类
    enum Gender {
        Male = 1,
        Female = 2,
    }
    let msg:{name:string,gender:Gender}  // 第二步,类型的时候填枚举类
    msg = {
        name:'zhangsan',
        gender:Gender.Female // 使用的时候用点语法
    }
    
  • 泛型
    • 不预先指定具体的类型,而在使用的时候再指定类型的一种特性
    type IType = <T>(target: T) => T[];
    
    • 泛型接口 & 多泛型
    interface IX<T, U> {
        key: T;
        val: U;
    }
    
    • 泛型类
    class IMan<T> {
        instance: T;
    }
    
    • 泛型别名
    type IArr<T> = Array<T>;
    
    • 泛型约束:extends。补充extends作用:
      • ①接口继承
      interface Animal { name: string }
      interface Dog extends Animal { bark: () => void }
      
      • ②普通条件判断:用来判断一个类型是否可以分配给另外一个类型。A extends B,是表示A类型能够分配给B类型,而不是表示A类型是B类型的子集。换句话来说,如果条件判断的时候A extends B为true,则说明约束A类型的一切约束条件,B类型都具有。
      type bool = Animal extends Dog ? 'yes': 'no' // bool = 'no'
      type bool1 = Dog extends Animal ? 'yes': 'no' // bool1 = 'yes'
      
      • ③泛型条件判断:对于使用extends关键字的条件类型,如果extends前面的参数是一个泛型类型,当传入该参数的是联合类型,则使用分配律计算最终的结果。
        • 分配律是指,将联合类型的联合项拆成单项,分别代入条件类型,然后将每个单项代入得到的结果再联合起来,得到最终的判断结果。
        • 满足两个要点即可适用分配律:
          • 参数是泛型类型,
          • 代入参数的是联合类型
      type AB<T> = T extends 'x' ? 'a' : 'b';
      type All = AB<'x' | 'y'>; // 非确定条件,可能是 'x' 或 'y'
      // 得到 type All = 'a' | 'b';
      
      • ④泛型约束
      function getInfo<T, key extends keyof T>(obj: T, key: key): T[key]{
          return obj[key]
      }
      const obj = {
          name: 'tom',
          age: 12
      }
      getInfo(obj, 'age') // 这里第二个参数只能传age或者name,传其他的字段都会报错。
      
  • 类型断言:as
    interface Vup {
        name: string,
        roomId: number,
    }
    // 以下代码报错,类型“{}”上不存在属性xxx
    // let azi = {}
    // azi.name = "azusa";
    // azi.age = 18;
    
    // 添加类型断言编译通过
    let azi = {} as Vup;
    azi.name = "azusa";
    azi.age = 18;
    
  • 字符串/数字 字面量
    • 允许指定字符串/数字必须的固定值
    type IChar = 'A' | 'B' | 'C';
    type INum = 1 | 3 | 5;
    

3、高级类型

  • 联合/交叉类型
    • 联合类型:IA|IB,表示一个值可以是几种类型之一
    • 交叉类型:IA&IB,多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
    const vupList = [{
        name: 'azusa',
        type: 'human',
        vrlink: 1
    }, {
        name: 'nanami',
        type: 'shark',
        vrp: 3,
    }]
    
    type IVupList = Array<{
        name: string;
    } & ({
        type: 'human';
        vrlink: number;
    } | {
        type: 'shark';
        vrp: number;
    })>
    
  • 类型保护与类型守卫
    • 类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域
    interface IA { a: 1, a1: 2}
    interface IB { b: 1, b1: 2}
    
    // 类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域
    function getIsIA(arg: IA|IB): arg is IA {
        return !!(arg as IA).a;
    }
    function log2(arg: IA|IB) {
        if(getIsIA(arg)) {
            console.log(arg.a1);
        } else {
            console.log(arg.b1);
        }
    }
    
    • 实现logVup方法,函数接收vup类型,并打印出相关特征
      • 联合类型 + 类型保护 = 自动类型推断
    function logVup(vup: IVup) {
        if (vup.type === 'human') {
            console.log(vup.vrlink);
        } else {
            console.log(vup.vrp);
        }
    }
    
  • 高级类型
    • 假如现在要把两个对象 sourceObj 和 targetObj 合并,其类型实现繁琐。若 obj 类型复杂,则生命 source 和 target 便需要大量重复2遍。若 target 增加/减少 key, 则需要 source 联动去除
    interface ISourceObj {
        x?: string;
        y?: string;
    }
    interface ITargetObj {
        x: string;
        y: string;
    }
    type IMerge = (sourceOBj: ISourceObj, targetObj: ITargetObj) => ITargetObj;
    
    • 使用高级类型RecordPartial
    interface IMerge {
        <T extends Record<string, any>>(sourceObj: Partial<T>, targetObj: T): T;
    }
    
    • Partial的底层实现
      • keyof:取值对象中的所有 key 组成的字符串字面量。
        • e.g.:type IKeys = keyof {a: string, b: number};等价于type IKeys = 'a'|'b';
      • in:遍历
      • ?:将该属性变为可选属性
    type Partial<T> = {
        [P in keyof T]?: T[P];
    }
    
  • 函数返回值类型
    • 实现一个delayCall函数,接收一个函数作为入参,返回一个promise,结果为入参函数的返回结果
    function delayCall(func) {
        return new Promise(resolve => {
            setTimeout(()=>{
                const result = func();
                resolve(result);
            }, 1000);
        });
    }
    
    • 定义类型
      • extends跟随泛型出现时,表示类型推断,其表达可类比三元表达式,如T === 判断类型 ? 类型A: 类型B
      • infer的理解:如果 T 继承了 extends (...args: any[]) => any 类型,则返回类型 R,否则返回 any。其中 R 被定义在 extends (...args: any[]) => infer R 中,即 R 是从传入参数类型中推导出来的。
    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的作用、TypeScript的高级类型都很难懂,也不容易想明白,需要时常回顾复习。

五、引用参考