typescript 类型系统

对于前端来说,最大的优势其实就是TS的类型系统。

 参考:https://www.tslang.cn/docs/handbook/basic-types.html【官网】 或  http://ts.xcatliu.com/basics/index.html(推荐)

一、基础类型(原始类型):

  • 布尔值:布尔值是最基础的数据类型,在 TypeScript 中,使用 boolean 定义布尔值类型
    let isDone: boolean = false;
  • 数字:使用 number 定义数值类型
    let decLiteral: number = 6;
    let hexLiteral: number = 0xf00d;
    // ES6 中的二进制表示法
    let binaryLiteral: number = 0b1010;
    // ES6 中的八进制表示法
    let octalLiteral: number = 0o744;
    let notANumber: number = NaN;
    let infinityNumber: number = Infinity;
  • 字符串:使用 string 定义字符串类型
    let myName: string = 'Tom';
    let myAge: number = 25;
    
    // 模板字符串
    let sentence: string = `Hello`;
  • 数组:
    let list: number[] = [1, 2, 3];

    或,使用数组泛型,Array<元素类型>

    let list: Array<number> = [1, 2, 3];
  • 元组 Tuple:元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 
    // Declare a tuple type
    let x: [string, number];
    // Initialize it
    x = ['hello', 10]; // OK
    // Initialize it incorrectly
    x = [10, 'hello']; // Error
  • 枚举:使用枚举类型可以为一组数值赋予友好的名字。【就是字典数据】
    enum Color {Red, Green, Blue} // 默认情况下,从0开始为元素编号。即 {Red = 0, Green = 1, Blue = 2}
    let c: Color = Color.Green;

    或者,全部都采用手动赋值:

    enum Color {Red = 1, Green = 2, Blue = 4}
    let c: Color = Color.Green;

    枚举类型提供的一个便利是你可以由枚举的值得到它的名字。即反过来 通过 值 获取 名字。这一点是我们使用字典对象,常用的功能。

    enum Color {Red = 1, Green, Blue}
    let colorName: string = Color[2];
    
    console.log(colorName);  // 显示'Green'因为上面代码里它的值是2
  • Void:它表示没有任何类型,当一个函数没有返回值时,你通常会见到其返回值类型是 void
    function warnUser(): void {
        console.log("This is my warning message");
    }

    声明一个void类型的变量没有什么大用,因为你只能为它赋予undefinednull。所以,void就是声明函数没有返回值。

    let unusable: void = undefined;
  • Null 和 Undefined:在 TypeScript 中,可以使用 null 和 undefined 来定义这两个原始数据类型
    let u: undefined = undefined;
    let n: null = null;

    与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:

    // 这样不会报错
    let num: number = undefined;

    而 void 类型的变量不能赋值给 number 类型的变量:

    let u: void;
    let num: number = u;
    
    // Type 'void' is not assignable to type 'number'.

二、泛型   http://ts.xcatliu.com/advanced/generics.html  (这里简单介绍,详细看下面的 进阶篇)

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray<string>(3, 'x'); // ['x', 'x', 'x']

总结:

  • 在调用的时候,指定它具体的类型,就可以确定最后返回的是什么类型的数据了。
  • 函数的泛型参数也是可以预设默认值的。
    // 这里设置    泛型 T 的默认类型是 string
    function createArray<T = string>(length: number, value: T): Array<T> {
        let result: T[] = [];
        for (let i = 0; i < length; i++) {
            result[i] = value;
        }
        return result;
    }
  • 使用时传入的泛型参数,类似函数传入的参数。

三、类型推断      http://ts.xcatliu.com/basics/type-inference.html

如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。所以ts中有的变量没有写明类型。

四、联合类型       http://ts.xcatliu.com/basics/union-types.html

联合类型(Union Types)表示取值可以为多种类型中的一种。

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
  • 访问联合类型的属性或方法
    当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法

五、对象的类型--接口      http://ts.xcatliu.com/basics/type-of-object-interfaces.html

  在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码 定义契约

  • 可选属性:接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。
    interface SquareConfig {
      color?: string;
      width?: number;
    }
    
    function createSquare(config: SquareConfig): {color: string; area: number} {
      let newSquare = {color: "white", area: 100};
      if (config.color) {
        newSquare.color = config.color;
      }
      if (config.width) {
        newSquare.area = config.width * config.width;
      }
      return newSquare;
    }
    
    let mySquare = createSquare({color: "black"});

    带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。

  • 只读属性:一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性。
    interface Point {
        readonly x: number;
        readonly y: number;
    }
    
    let p1: Point = { x: 10, y: 20 };
    p1.x = 5; // error!

    TypeScript具有ReadonlyArray<T>类型,它与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改

    let a: number[] = [1, 2, 3, 4];
    let ro: ReadonlyArray<number> = a;
    ro[0] = 12; // error!
    ro.push(5); // error!
    ro.length = 100; // error!
    a = ro; // error!
  • 任意属性:一个接口可能需要它除了具有我们需要的属性以为,还可以包含任意的其他属性,这时就要用到任意属性
    interface Person {
      name: string;
      age?: number;
      // 这种方式也叫 字符串索引签名
      [propName: string]: number | string;
    }
    let tom: Person = {
      name: 'Tommy',
      addr: '北京'
    };
    console.log(tom);
  • 函数类型:接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。
    interface SearchFunc {
      (source: string, subString: string): boolean;
    }

    这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。

    let mySearch: SearchFunc;
    mySearch = function(source: string, subString: string) {
      let result = source.search(subString);
      return result > -1;
    }
  • 可索引属性
    interface StringArray {
      [index: number]: string;
    }
    
    let myArray: StringArray;
    myArray = ["Bob", "Fred"];
    
    let myStr: string = myArray[0];
  • 类 接口
    class Point {
        x: number;
        y: number;
    }
    interface Point3d extends Point {
        z: number;
    }
    let point3d: Point3d = {x: 1, y: 2, z: 3};
  • 泛型接口: 查看 下面 泛型的介绍
  • 接口继承接口
    接口的继承 可以 实现接口 的复用
  • 接口继承类
  • 类实现接口

  总结:一个接口可以作为另一个接口属性的类型,这样就可以给一个多层级对象进行类型声明了。如,

// 接口返回列表数据,每个列表的接口类型
export interface CategoryObj {
    id: number | string
    name: string
    category1Id?: number
    category2Id?: number
}

// 接口完整的 返回数据对应的接口类型
export interface CategoryResponseData {
    data: CategoryObj[],
    code: number
    message: string
    ok: boolean
}

六、类型别名:type 关键字   http://ts.xcatliu.com/advanced/type-aliases.html

类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值。如,联合类型,元组以及其它任何你需要手写的类型。

type Name = string;  // 给原始类型起别名通常没什么用
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    }
    else {
        return n();
    }
}

起别名不会新建一个类型 - 它创建了一个新 名字来引用那个类型。 给原始类型起别名通常没什么用,尽管可以做为文档的一种形式使用。

重点:

  • 类型别名 和 接口 的区别:和接口一样,用来描述对象或函数的类型。一般我们推荐使用 interface,但有些情况还是用 type 方便。

  • 类型别名用的地方:无法通过接口来描述一个类型并且需要使用联合类型或元组类型,这时通常会使用类型别名。
    • 可以声明联合类型,如 type paramType = number | string;
      个人:就一个联合类型,用 接口 定义 感觉太重,用 别名刚刚好。定义好后,其他地方都可以使用。
    • 可以声明元组类型,如type arrType = [string, string, number]
    • 定义对象时严谨的来说,type 是引用,而 interface是定义。

七、数组的类型    http://ts.xcatliu.com/basics/type-of-array.html

 

八、函数的类型   http://ts.xcatliu.com/basics/type-of-function.html

  • 为函数定义类型

    //  声明式  函数
    function add(x: number, y: number): number {
        return x + y;
    }
    
    // 表达式  函数
    let myAdd = function(x: number, y: number): number { return x + y; };

九、promise对象的类型

  • 返回值基础类型
    // 声明一个 Promise,成功时返回一个 number 类型的值
    let promise: Promise<number>;
  • 返回对象
    interface backResult{
        code: number,
        data: { name:string,age:number}[], //数组里面的对象类型,这里使用的是类型
        message:string
    }
    // 在这里声明出promise的类型,使用的接口
    let p: Promise<backResult> = new Promise((resolve,reject)=> { 
        resolve({
            code: 200,
            data: [
                {name:'张三',age:123}
            ],
            message:'操作成功'
        })
    })
    p.then((res) => { 
        if (res.code == 200) { 
            let arr = res.data.map(v => v.name)
        }
    })

   个人:这里promise泛型来表示返回值感觉和函数中的泛型感觉是不一样的。函数中的泛型就是类型变量,而promise的泛型表示的就是返回值的类型。

十、类型断言    http://ts.xcatliu.com/basics/type-assertion.html

型断言(Type Assertion)可以用来手动指定一个值的类型。

 


 

进阶篇

十一、泛型

  软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型。

  简单来说:泛型就是一个类型占位符(一个不确定的类型),其它地方也会 根据这个类型 设定 相应的类型

  语法:尖括号表示定义了若干个泛型变量,只有定义了这个变量,后面才能使用它。使用的时候也需要 写明 泛型变量类型,因为ts有类型推断功能,有的情况在使用泛型时就不需要再写明数据类型了。

function identity<T>(arg: T): T {
    return arg;
}
  • 泛型函数
    function fn<T>(a:T): T{
        return a;
    }
    
    // 可以直接调用具有泛型的函数
    fn(‘hello’)  // 不指定泛型,TS 可以自动对类型进行推断
    
    fn<string>('hell0')  // 指定泛型

    表达式函数写法

    const printFun = <T>(value: T): T => {
      console.log(value);
      return value;
    };
    
    printFun(233);
  • 泛型接口
    interface Person<J, K> {
      name: J;
      age: K;
    }
    
    // 这里使用 泛型接口时,需要写明此时 泛型对应的 数据类型
    const me: Person<string, number> = {
      name: "sunny",
      age: 18,
    };

    泛型接口中,泛型接口有类似函数。在使用的时候需要写明使用时 泛型 变量对应的 数据类型

 

 

 

posted @ 2020-07-28 08:48  吴飞ff  阅读(150)  评论(0编辑  收藏  举报