typescript基础

88 阅读8分钟

1. 类型

原始类型:Number,String,Boolean,null,undefined,Symbol

引用类型:Array,Function,Object

TypeScript在ES6的数据类型基础上,新增了以下数据类型:void, any, never, 元组,枚举,高级类型

  • null\undefined

    • null 和 undefined 是所有类型的子集
    • 被定义为null和undefined类型的,只能赋值为它本身
  • void

    • void类型可以被赋值为undefined和null,但是不能被赋值其他的,比如string
  • 数组

    • number[]
    • Array
    interface INumberArray{
        [index: number]: number;
    }
    
  • 元组

    元组限定了数组的类型和个数,可以通过push越界放入,但访问不到

    let a: [string, number]
    let b: ['666', 6666]
    
  • any

    可复制任意类型,不写相当于any

  • never

    用不存在的值的类型。例如:总会抛出异常或不会有返回值的函数表达式

    // 抛出异常
    let error: never = (() => {
       throw new Error(); 
    })()
    
    // 死循环
    let endLess: never = (() => {while(true){}})()
    
  • 联合类型

    let a = string | number;
    

    类型断言

    let a = string | number;
    console.log((<string>a).length);
    

    类型别名

    type newType = string | number
    let a: newType
    
  • enum枚举类型

    enum Gender {Male, Female}
    // {'0': 'Male', '1': 'Female', 'Male':0, 'Female':1}
    console.log(Gender['0']) // Male
    console.log(Gender['Male']) // 0
    

    数字枚举

    enum Direction{
        NORTH, // 默认0
        SOUTH, // 默认1
        EAST, // 默认2
        WEST, // 默认3
    }
    let dir: Direction = Direction.EAST;
    let dir = 4
    

    常量枚举

    const enum 普通枚举有对象,常量枚举没有对象,对象实体只存在于TS中,转译为ES5时删除

    用途:不需要该对象,只需要该对象的值时,适用常量枚举

    // TypeScript 代码
    const enum Gender {Male, Female}
    let a: Gender = Gender['Female']
    console.log(a) // 1
    // ES5 代码
    var a = 1 /* 'Female' */;
    console.log(a);
    

    异构枚举:除了数字枚举,还可以字符串枚举,也可以两者混用

    数字枚举比字符串枚举多了反向映射

2. 接口

  • 基础

    interface IPerson{
        name: string,
        age: number
    }    
    
  • 可选属性

    interface IPerson{
        name: string,
        age?: number
    } 
    
  • 只读属性

    interface IPerson{
       readonly name: string,
       age?: number
    } 
    
  • 任意属性

    interface IPerson{
        name: string,
        age?: number,
        [propName: string]: boolean | string
    } 
    

3. 函数

  • 四种定义方式

    • 函数声明
    function add(x:number, y:number): number{
        return x + y
    }
    
    • 函数表达式
    const add = (x:number, y:number): number{
        return x + y
    }
    
    • 别名
    type addFunc = (x:number, y:number): number{
        return x + y
    }
    let add: addFunc = (a,b) => a + b
    
    • 接口形式
    interface IAdd{
        (x: number, y: number): number
    }
    let add: IAdd
    
  • 可选参数

    const add = (x:number, y?:number): number{
        return x + (y || 0)
    }
    
  • 剩余参数

    const add = (...args: number[]) => {}
    
  • 函数重载

    只有一个函数,但可以对不同的参数组合进行静态类型检查

    function aFun(arr: number[])
    function aFun(arr: string[])
    function aFun(arr: any[]) {
        if (typeof(arr[0]) === 'number') {
            return arr.reduce((pre, cur) => pre + cur)
        } else if (typeof(arr[0]) === 'string') {
            return arr.reduce((pre, cur) => pre + cur)
        }
    }
    

4. 类

  • extends继承 - extends super
  • static - 通过构造函数调用
  • public - 类内外均可调用
  • protected - 只能由类或子类的函数访问
  • private - 只能由类本身的实例访问
  • abstract
    • 抽象类:不可实例化,不可直接new
    • 抽象父类中的抽象方法,子类继承时需要实现该抽象方法

5. 类和接口的关系

  • 类实现接口

    • 接口声明成员属性和方法,不做实现,类声明并实现方法
    • 同一个类可同时实现多个接口
    interface IA{
        a: number;
        f(x: number): void;
    }
    interface IA2{
        b: string;
    }
    class A implements IA, IA2{
        
    }
    
  • 接口继承接口

    interface IA{
        a: number;
        f(x: number): void;
    }
    interface IA2 extends IA{
        b: string;
    }
    class A implements IA2{
        
    }
    
  • 接口继承类

    接口继承类,另一个类实现接口

    class C1{
        a: number;
        f(x: number): void;
    }
    interface I1 extends C1{
        b: string;
    }
    class C2 implements I1{
        
    }
    

6. 泛型

定义函数、接口、类时,不预先指定具体类型,而是在使用时再指定类型。即使用时指定的类型在定义时的变量指代。

可提高代码的重用性

  • 泛型函数
    const a<T> = (a: T) : T => a
    const a<T, U> = (a: [T, U]): [T, U] => a;
    // 默认类型
    const a<T = string> = (length: number, value: T>: Array<T> => {}
    
  • 泛型接口 实现接口时必须指定类型
    interface Demo<T>{
        (val: T): T
    }
    
  • 泛型类
    class Demo<T> {
        value: T | undefined
        fn: ((x: T, y: T) => T) | undefined
    }
    
  • 泛型约束
    interface Length{
        length: number;
    }
    const a<T extends Length> = (a: T): T{}
    

7. 类型兼容

基于结构,只是用成员来描述类型

如果类型 x 要兼容 y, 那么 y 至少具有与 x 相同的属性。即 x 中的每个属性,在 y 中都能找到对应属性。(小类型兼容大类型)

interface X{
    a: string;
}
let x: X = {a: '123', b: 123}
  • 函数兼容
    • 函数参数 协变:子类型赋值给父类型 逆变:父类型赋值给子类型

      • 参数数量(协变)

      重点:函数可忽略参数(传参多的兼容传参少的)

      x 赋值给 y,需要确保 x 的每个参数在 y 里能找到对应类型的参数。

      let x = (a: number) => 0
      let y = (b: number, s: string) => 0;
      y = x;
      
      • 参数类型(逆变)

      重点:要保证有属性(属性少的兼容属性多的)

      interface a{
          name: string;
          age: number;
      }
      interface b{
          name: string;
      }
      let foo = (x: a, y: number) => {];
      let foo1 = (x: b) => {};
      foo = foo1
      
    • 函数返回值(协变)

  • 枚举兼容
    • 枚举类型与数字类型相互兼容
    • 不同枚举类型之间不兼容
  • 类兼容
    • 仅比较类的实例属性
    • 私有成员和受保护成员需来自同一个类
  • 泛型兼容
    • 作为类型参数,会影响适用其作为类型一部分的结果类型

8. 类型保护 / 类型守卫

在特定区块中保证变量属于某种确定的类型

  • instanceof
  • in
if('prop' in lang){
    lang.prop()
} 
  • 类型保护函数

类型谓词:parameter is Type

class Java{
    helloJava() {
        console.log('Java')
    }
}

class JavaScript{
    helloJavaScript() {
        console.log('JavaScript')
    }
}

// 类型保护函数
function isJava(lang: Java | JavaScript): lang is Java { // 类型谓词
    return (lang as Java).helloJava !== undefined
}
function typeGuardFunc(type: Type) {
    let lang = type === Type.Strong ? new Java() : new JavaScript
    if (isJava(lang)) {
        lang.helloJava()
    } else {
        lang.helloJavaScript()
    }
} 
  • typeof
  • 字面量守卫
if(lang.kind === 'Java'){} 

8. 高级类型

  • 联合类型
  • 交叉类型

多个类型的并集,将多个类型合并为一个类型

  • 索引类型

    • keyof type
    • type['prop']
    • 泛型约束
    interface t{
        a: number,
        b: string
    }
    let key: keyof t
    let value: t['a']
    // 遍历对象 - 同时保持对象属性和属性值的类型约束
    function pluck<T, K extends keyof T>(o: T, names: K[]){
        return names.map(item => o[item])
    }
    
  • 映射类型

    从一旧类型生成新类型,实质为泛型接口

    interface PersonPartial{
        name?: string;
        age?: name;
    }
    type Readonly<T> = {
        readonly [P in keyof T]: T[P];
    }
    type Partial<T> = {
        [P in keyof T]?: T[P]
    }
    type Keys = 'option1' | 'option2'
    type Flags = { [K in Keys]: boolean }
    

    in操作符内部适用了 for……in……

  • 条件类型

    type TypeName = T extends string ? 'string' : 'number';

    • 联合类型 + 条件类型

    <A | B> extends U ? X : Y

    会转化为

    (A extends U ? X | Y) | (B extends U ? X : Y);

9. 声明合并

  • 接口合并
    • 非函数成员
    非函数成员必须唯一,不唯一则需要类型一致
    interface A{
        height: number;
    }
    interface A{
        width: string;
    }
    
    • 函数成员
      • 合并,相当于函数重载
      • 重载顺序:后来居上,接口内部按书写顺序
      • 特殊优先顺序:参数的类型是单一的字符串字面量
      interface A {
          foo(bar: number): number; // 5
          foo(bar: 'a'): string; // 2
          }
      
      interface A {
          foo(bar: string): string; // 3
          foo(bar: string[]): string[]; // 4
          foo(bar: 'b'): string; // 1
      }
      
          // 合并后的等价效果
      interface Cloner {
          foo(bar: 'b'): string;
          foo(bar: 'a'): string;
          foo(bar: string): string;
          foo(bar: string[]): string[];
          foo(bar: number): number;
      }
      

10. 模版字面量类型

在模版字符串中使用extends

type StartsWith<Str extends string, Prefix extends string> = 
Str extends `${Prefix}${string}` ? true : false;

11. 分配条件类型

使用条件类型时,如果左侧的参数为泛型类型,且传入的该参数为联合类型,条件类型将会作用到该联合类型的每一个成员。

类似于使用分配率,将联合类型拆分为单项,分别代入条件类型,最后将各单项的代入得到的结果联合作为最终结果。

type StrOrNum = string | number
type Result1 = StrOrNum extends string ? string : number // number
type Generic<T> = T extends string ? string : number

// 使用泛型,泛型参数为联合类型
type Result2 = Generic<StrOrNum> // string | number

// 等价于
type Type1 = Generic<string>
type Type2 = Generic<number>
type Result3 = Type1 | Type2 // string | number

若要避免这种行为,可以用方括号将 extends 关键字的每一侧括起来。

// 不希望使用分配条件类型
type Generic2<T> = [T] extends [string] ? string : number;

type Result4 = Generic2<StrOrNum> // number

12. infer

在extends条件类型,还可以使用infer关键字,可以用于推断一个类型变量,但该变量只能在条件类型的true分支上使用。

type ItemType<T>  = T extends (infer R)? R : never;

type temp1 = ItemType<number[]> // number
type temp2 = ItemType<string[]> // string
type temp3 = ItemType<[1,2,3]> // 1 | 2 | 3

可在一个条件类型表达式中推断多个类型变量

type FuncType = (a: string, b: number) => boolean

type FuncValueAndReturn<T extends function> = 
T extends (a: infer A, b: infer B, ...args: any[]) => infer C ? [A, B, C]: never

type result = FuncValueAndReturn<FuncType> // [string, number, boolean]

在一个条件类型表达式中多处使用同一个类型变量

  • 协变 在协变位置上,同一个类型变量的多个候选类型会被推断为联合类型
type ItemType2<T extends any[]> = T extends [infer R, infer R, ...any[]] ? R : never;

type temp2 = ItemType2<[number, string]> // string | number
type temp2 = ItemType2<[1, 2, 3]> // 1 | 2
  • 逆变 在逆变位置上,同一个类型变量的多个候选类型会被推断为交叉类型
type FuncType = (a: string, b: number) => boolean

type FuncValueAndReturn2<T extends function> = 

T extends (a: infer A, b: infer A) => infer C ? [A, C] : never;



type result2 = FuncValueAndReturn2<FuncType> // [string & number, boolean]

在模板字符串中使用infer

type ReplaceStr<
    Str extends string,
    From extends string,
    To extends string
> = Str extends `${infer Prefix}${From}${infer Suffix}` 
        ? `${Prefix}${To}${Suffix}` : Str;

type ReplaceResult = ReplaceStr<'javascript','java','type'>