美文网首页TypeScript
TypeScript的数据类型

TypeScript的数据类型

作者: 浅忆_0810 | 来源:发表于2020-10-09 23:10 被阅读0次

    TypeScipt中为了使编写的代码更加规范,更有利于维护,增加了类型校验,所以以后写ts代码必须要指定类型

    TypeScript后面可以不指定类型,但是后期不能改变其类型,不然会报错,但是只会报错,不会阻止代码编译,因为JS是可以允许的


    1. 基本类型

    1.1 布尔类型(boolean)

    let flag: boolean = true;
    flag = 123; // 报错,因为不符合类型
    flag = false; // 正确写法,boolean类型只能写 true 和 false
    

    1.2 数字类型(number)

    let num: number = 123;
    console.log(num);
    

    1.3 字符串类型(string)

    let str: string = "string";
    console.log(str);
    

    1.4 数组类型(Array)

    TypeScript中有两种定义数组的方法

    // 第一种定义数组方法
    let arr1: number[] = [1.2.3]; // 一个全是数字的数组
    
    // 第二种定义数组方法
    let arr2: Array<number> = [1,2,3];
    

    1.5 只读数组类型(ReadonlyArray)

    只读数组中的数组成员和数组本身的长度等属性都不能够修改,并且也不能赋值给原赋值的数组

    let a: number[] = [1,2,3,4];
    let ro: ReadonlyArray<number> = a; // 其实只读数组只是一个内置定义的泛型接口
    ro[1] = 5; // 报错
    ro.length = 5; // 报错
    a = ro; // 报错,因为ro的类型为Readonly,已经改变了类型
    a = ro as number[]; // 正确,不能通过上面的方法复制,但是可以通过类型断言的方式
    

    类型断言见此链接

    1.6 元组类型(tuple)

    元组类型属于数组的一种,上面一种数组里面只能有一种类型,否则会报错,而元组类型内部可以有多种类型

    // 元组类型可以为数组中的每个成员指定一个对应的类型
    let arr: [number,string] = [123,"string"]; // 注意如果类型不对应也会报错
    arr[2] = 456; // 报错
    // 因为元组类型在声明时是一一对应的,这里只能有2个成员
    

    1.7 枚举类型(enum)

    /*
      通过:
        enum 枚举名{
          标识符 = 整形常数,
          标识符 = 整形常数,
          ......
          标识符 = 整形常数
        };
        定义一个枚举类型
    */
    enum Color {
      Red = 1,
      Blue = 3,
      Oragne = 5
    }
    
    let color: Color = Color.Red; // color为Color枚举类型,值为Color中的Red,也就是1
    console.log(color); // 1
    

    注意:

    • 如果没有给标识符赋值,那么标识符的值默认为索引值
    • 如果在期间给某个标识符进行了赋值,而之后的标识符没有赋值,那么之后表示符的索引依次为前面的值加1
    • 可以把标识符用引号括起来,效果不受影响
    • 还可以通过反过来选择索引值来获取字符串标识符
    enum Color {
      Red,
      Blue = 3,
      "Oragne"
    }
    
    let color1: Color = Color.Red;
    let color2: Color = Color.Blue;
    let color3: Color = Color.Oragne;
    let color4: string = Color[0]; // 通过获取索引得到标识符
    
    console.log(color1); // 0
    console.log(color2); // 3
    console.log(color3); // 4
    console.log(color4); // red
    

    1.8 任意类型(any)

    任意类型的数据就像JS一样,可以随意改变其类型

    let num:any = 123;
    num = "string";
    console.log(num); // string
    

    具体用法

    // 如果在ts中想要获取 DOM 节点,就需要使用任意类型
    let oDiv: any = document.getElementsByTagName("div")[0];
    oDiv.style.backgroundColor = "red";
    // 按道理说DOM节点应该是个对象,但是TypeScript中没有对象的基本类型,所以必须使用 any 类型才不会报错
    

    1.9 undefinednull类型

    虽然为变量指定了类型,但是如果不赋值的话默认该变量还是undefined类型,如果没有指定undefined直接使用该变量的话会报错,只有自己指定的是undefined类型才不会报错

    let flag1: undefined; // 也可以 let flag1: undefined = undefined;
    console.log(flag); // undefined
    
    let flag2: null = null; // 如果不指定值为null那么打印的时候会报错
    console.log(flag2); // null
    

    为变量指定多种可能的类型

    let flag: number | undefined; // 这种写法就不会在没有赋值的时候报错了,因为设置了可能为undefined
    console.log(flag); // undefined
    flag = 123;
    console.log(flag); // 123 也可以改为数值类型
    
    // 也可以设定多个类型
    let flag1: number | string | null | undefined;
    flag1 = 123;
    console.log(flag1); // 123
    flag1 = null;
    console.log(flag1); // null
    flag1 = "string";
    console.log(flag1); // string
    

    1.10 void类型

    TypeScript中的void表示没有任何类型,一般用于定义方法的时候没有返回值,虽然也能给变量指定类型,但是void类型只能被赋值undefinednull,没有什么实际意义

    TypeScript中函数的类型表示其返回值的类型,函数类型为void表示其没有返回值

    function run(): void {
      console.log(123);
      // return 不能有返回值,否则报错
    }
    
    function run1(): number {
      console.log(456);
      return 123; // 必须有返回值,并且返回值为number类型,否则报错
    }
    
    function run2(): any {
      console.log(789); // 因为any是任意类型,所以也可以不要返回值
    }
    

    1.11 never类型

    never类型是其他类型(包括nullundefine)的子类型,代表着从来不会出现的值,意味着声明never类型的变量只能被never类型所赋值

    let a: undefined;
    a = undefined;
    
    let b: null;
    b = null;
    
    let c: never; // c不能被任何值赋值,包括 null 和 undefiend
    c = (() => {
      throw new Error("error!!");
    })(); // 可以这样赋值
    
    // 返回never的函数必须存在无法达到的终点
    function error(message: string): never {
      throw new Error(message);
    }
    // 返回never的函数必须存在无法达到的终点
    function infiniteLoop(): never {
      while (true) {
      }
    }
    
    // 推断的返回值类型为never
    function fail() {
      return error("Something failed");
    }
    

    1.12 object类型

    object表示非原始类型,也就是除numberstringbooleansymbolnullundefined之外的类型

    let stu: object = {name:"张三", age:18};
    console.log(stu); // {name: "张三", age: 18}
    
    // 也可以
    let stu: {} = { name: "张三", age: 18 };
    console.log(stu); // {name: "张三", age: 18}
    
    declare function create (o: object | null): void;
    // declare是一个声明的关键字,可以写在声明一个变量时解决命名冲突的问题
    create({ prop: 0 }); // OK
    create(null); // OK
    
    create(42); // 报错
    create("string"); // 报错
    create(false); // 报错
    create(undefined); // 报错
    

    2. 高级类型

    2.1 交叉类型

    交叉类型是将多个类型合并为一个类型。这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性

    function extend<T, U>(first: T, second: U): T & U {
      let result = <any>{}; // 官方这里是T&U,但是会报错,因为不知T和U是不是对象,必须any才可以
    
      for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
      }
      for (let id in second) {
        if (!result.hasOwnProperty(id)) {
          (<any>result)[id] = (<any>second)[id];
        }
      }
    
      return result;
    }
    
    class Person {
      constructor(public name: string) { }
    }
    interface Loggable {
      log(): void;
    }
    class ConsoleLogger implements Loggable {
      log() {
        // ...
      }
    }
    
    var jim = extend(new Person("Jim"), new ConsoleLogger());
    var n = jim.name;
    jim.log();
    

    2.2 联合类型

    联合类型与交叉类型很有关联,但是使用上却完全不同,我们使用的用竖线隔开每一个类型参数的时候使用的就是联合类型

    联合类型表示一个值可以是几种类型之一。 用竖线( |)分隔每个类型,所以 number | string | boolean表示一个值可以是 numberstring,或 boolean

    function padLeft(value: string, padding: string | number) {
      // ...
    }
    
    let indentedString = padLeft("Hello world", true); // errors,只能是string 或 number
    

    如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员

    interface Bird {
      fly();
      layEggs();
    }
    
    interface Fish {
      swim();
      layEggs();
    }
    
    function getSmallPet(): Fish | Bird { // 这的报错先不管
      // ...
    }
    
    let pet = getSmallPet(); // 因为没有明确返回哪个类型,所以只能确定时Fish和Bird类型中的一个
    pet.layEggs(); // 我们可以调用共有的方法
    pet.swim();    // errors,不能调用一个类型的方法,因为万一是Bird类型就不行了
    

    2.3 类型保护

    联合类型适合于那些值可以为不同类型的情况。 但当我们想确切地了解是否为 Fish时,JavaScript里常用来区分2个可能值的方法是检查成员是否存在,但是在TypeScript中必须要先使用类型断言

    let pet = getSmallPet();
    
    if ((<Fish>pet).swim) {
      (<Fish>pet).swim();
    } else {
      (<Bird>pet).fly();
    }
    
    2.3.1 用户自定义类型保护
    function isFish(pet: Fish | Bird): pet is Fish {
      return (<Fish>pet).swim !== undefined; // 返回一个布尔值,然后TypeScript会缩减该变量类型
    }
    /*
      pet is Fish就是类型谓词。谓词为 parameterName is Type 这种形式,parameterName必须是来自于当前函数签名里的一个参数名
    */
    
    // 'swim' 和 'fly' 调用都没有问题了,因为缩减了pet的类型
    if (isFish(pet)) {
      pet.swim();
    } else {
      pet.fly();
    }
    

    TypeScript不仅知道在 if分支里 petFish类型,它还清楚在 else分支里,一定 不是 Fish类型,一定是 Bird类型

    2.3.2 typeof类型保护
    function padLeft(value: string, padding: string | number) {
      if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
      }
      if (typeof padding === "string") {
        return padding + value;
      }
        
      throw new Error(`Expected string or number, got '${padding}'.`);
    }
    

    这些 typeof类型保护只有两种形式能被识别:typeof v === "typename"typeof v !== "typename""typename"必须是 "number""string""boolean""symbol"。 但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护

    2.3.3 instanceof类型保护
    interface Padder {
      getPaddingString(): string
    }
    
    class SpaceRepeatingPadder implements Padder {
      constructor(private numSpaces: number) { }
      getPaddingString() {
        return Array(this.numSpaces + 1).join(" ");
      }
    }
    
    class StringPadder implements Padder {
      constructor(private value: string) { }
      getPaddingString() {
        return this.value;
      }
    }
    
    function getRandomPadder() {
      return Math.random() < 0.5
        ? new SpaceRepeatingPadder(4)
        : new StringPadder(" ");
    }
    
    // 类型为SpaceRepeatingPadder | StringPadder
    let padder: Padder = getRandomPadder();
    
    if (padder instanceof SpaceRepeatingPadder) {
      padder; // 类型细化为'SpaceRepeatingPadder'
    }
    if (padder instanceof StringPadder) {
      padder; // 类型细化为'StringPadder'
    }
    

    instanceof的右侧要求是一个构造函数,TypeScript将细化为:

    • 此构造函数的 prototype属性的类型,如果它的类型不为 any的话
    • 构造签名所返回的类型的联合
    2.3.4 可以为null的类型

    如果没有在vscode中,直接编译的话是可以给一个其他类型的值赋值为undefined或者null的,但是如果编译时使用了--strictNullChecks标记的话,就会和vscode一样不能赋值了,并且可选参数和可以选属性会自动加上| undefined类型

    2.3.5 类型保护与类型断言

    由于可以为null的类型是通过联合类型实现,那么需要使用类型保护来去除 null

    function f(sn: string | null): string {
      if (sn === null) {
        return "default";
      } else {
        return sn;
      }
    }
    
    // 也可以使用下面的形式
    function f(sn: string | null): string {
      return sn || "default";
    }
    

    如果编译器不能够去除 nullundefined,你可以使用类型断言手动去除。 语法是添加 !后缀:identifier!identifier的类型里去除了 nullundefined

    function broken(name: string | null): string {
      function postfix(epithet: string) {
        return name.charAt(0) + '.  the ' + epithet; // error, 'name' is possibly null
      }
      name = name || "Bob";
    
      return postfix("great");
    }
    
    // 上面的函数会报错
    function fixed(name: string | null): string {
      function postfix(epithet: string) {
        // 这里的name强制断言不是null或者undefined,因为在嵌套函数中不知道name是否为null
        return name!.charAt(0) + '.  the ' + epithet; // ok
      }
      name = name || "Bob"; // 这里已经明确表明返回的name肯定是字符串
      
      return postfix("great"); // 但是由于是嵌套函数,这里还不知道
    }
    /*
      嵌套函数中: 因为编译器无法去除嵌套函数的null(除非是立即调用的函数表达式)。因为它无法跟踪所有对嵌套函数的调用,尤其是将内层函数做为外层函数的返回值。如果无法知道函数在哪里被调用,就无法知道调用时name的类型
    */
    

    2.4 类型别名

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

    别名不会创建一个新的类型,而是创建了一个新的名字来引用那个类型

    type Name = string; // 通过type关键词创建一个别名
    type NameResolver = () => string;
    type NameOrResolver = Name | NameResolver;
    function getName(n: NameOrResolver): Name {
      if (typeof n === 'string') {
        return n;
      } else {
        return n();
      }
    }
    

    同接口一样,类型别名也可以是泛型,可以添加类型参数并且在别名声明的右侧传入

    type Container<T> = { value: T };
    
    // 我们也可以使用类型别名来在属性里引用自己
    type Tree<T> = {
      value: T;
      left: Tree<T>;
      right: Tree<T>;
    }
    
    // 与交叉类型一起使用,我们可以创建出一些十分稀奇古怪的类型
    type LinkedList<T> = T & { next: LinkedList<T> };
    
    interface Person {
      name: string;
    }
    
    var people: LinkedList<Person>;
    var s = people.name; // 注意这种写法在vscode中会报错,因为更加严格,必须先赋值再使用
    var s = people.next.name;
    var s = people.next.next.name;
    var s = people.next.next.next.name;
    

    类型别名不能出现在声明右侧的任何地方

    type Yikes = Array<Yikes>; // 报错
    

    2.5 字符串自变量类型

    字符串字面量类型允许指定字符串必须的固定值。在实际应用中,字符串字面量类型可以与联合类型、类型保护和类型别名很好的配合。通过结合使用这些特性,可以实现类似枚举类型的字符串

    type Easing = "ease-in" | "ease-out" | "ease-in-out";
    
    class UIElement {
      animate(dx: number, dy: number, easing: Easing) {
        if (easing === "ease-in") {
          // ...
        } else if (easing === "ease-out") {
        } else if (easing === "ease-in-out") {
        } else {
          // error! should not pass null or undefined.
        }
      }
    }
    
    let button = new UIElement();
    button.animate(0, 0, "ease-in");
    button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
    // 只能从三种允许的字符中选择其一来做为参数传递,传入其它值则会产生错误
    

    字符串字面量类型还可以用于区分函数重载

    function createElement(tagName: "img"): HTMLImageElement;
    function createElement(tagName: "input"): HTMLInputElement;
    // ... more overloads ...
    function createElement(tagName: string): Element {
      // ... code goes here ...
    }
    

    相关文章

      网友评论

        本文标题:TypeScript的数据类型

        本文链接:https://www.haomeiwen.com/subject/syekpktx.html