TypeScript入门学习@郝晨光

作者: 郝晨光 | 来源:发表于2019-07-29 00:44 被阅读38次

    前言

    现在TypeScript越来越火,咱也赶一下潮流,开始学习一下TypeScript,在学习的同时做笔记记录,希望可以共同学习,在未来的程序生涯中可以使用TypeScript来进行开发。

    TypeScript是什么?

    TypeScript 是 JavaScript 的一个超集,主要提供了类型系统对 ES6 的支持,因为JavaScript是弱类型语言,我们在开发的时候,很容易会因为数据类型的原因造成一些不明不白的错误,所以,在TypeScript中,对类型的检测是极为严格的。

    TypeScript的优势

    在这里我借用阮一峰大神的文章来说一下TypeScript的优势。

    1. TypeScript 增加了代码的可读性和可维护性;

      • 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了
      • 可以在编译阶段就发现大部分错误,这总比在运行时候出错好
      • 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等
    2. TypeScript 非常包容;

      • TypeScript 是 JavaScript 的超集,.js 文件可以直接重命名为 .ts 即可
      • 即使不显式的定义类型,也能够自动做出类型推论
      • 可以定义从简单到复杂的几乎一切类型
      • 即使 TypeScript 编译报错,也可以生成 JavaScript 文件
      • 兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取
    3. TypeScript 拥有活跃的社区;

      • 大部分第三方库都有提供给 TypeScript 的类型定义文件
      • Google 开发的 Angular2 就是使用 TypeScript 编写的
      • TypeScript 拥抱了 ES6 规范,也支持部分 ESNext 草案的规范

    TypeScript的缺点

    • 无非就是难学了一点,无所谓啦,反正再难学还是要学的!自己选的路,跪着也要走下去。

    写在前边

    首先一定要从JavaScript的弱类型思想中走出来,在TypeScript中,保证在大部分时候都要给数据定义类型。
    其次一定要坚持,坚持,坚持。
    那就先从基础开始吧!


    安装使用

    1. 首先全局安装typescript
      npm install typescript -g
      
    2. 当全局安装后,我们就可以在全局下使用tsc指令了
      先通过tsc -v查看一下当前的TypeScript的版本号,顺便看一下有没有安装成功。
      tsc -v
      
    3. 接着新建一个文件夹,新建一个.ts文件,例如: index.ts;
      在使用TypeScript时,我们使用.ts为后缀的文件,如果是使用React的时候,我们默认使用.tsx为后缀的文件。它们分别对应的是.js.jsx文件。
    4. index.ts文件中,写入如下代码
      function sayHello(person: string) {
          return 'Hello' + person;
      }
      let person = [1, 2, 3];
      sayHello(person);
      
    5. 打开命令提示符,通过如下指令编译TypeScript文件
      tsc index.ts
      
    6. 执行会发现命令提示符报错index.ts:5:10 - error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string',因为我们给函数的参数定义成为string类型,而我们传入的是一个数组,所以会报错。
    7. 但是虽然报错了,依旧生成了index.js文件,这是因为TypeScript文件就算编译出错也依旧会编译成js文件,毕竟这点错误在咱们的JavaScript里边不算什么。
    8. 接着修改我们的index.ts文件
      function sayHello(person: string) {
          return 'Hello' + person;
      }
      let person: string = '郝晨光';
      sayHello(person);
      
    9. 再次执行tsc index.ts指令
    10. 这次命令提示符没有任何的错误信息,并且可以看到生成的index.js文件。

    基本类型

    对于基本类型,直接定义类型即可。定义好就必须指定类型,不可以变成其它类型。

    1. 定义布尔类型的值
      // 布尔值
      let isDone: boolean = true;
      
    2. 定义数字类型的值
      // 数字
      let count: number = 1;
      
    3. 定义字符串类型的值
      // 字符串
      let str:string = 'typescript';
      
    4. 定义空值
      空值是typescript中新引入的一种基本类型,可以用 void 表示没有任何返回值的函数。
      function alertName(): void {
          alert('郝晨光加油!')
      }
      
      或者声明一个空值,但是这样毫无意义,因为只能被赋值为undefined和null。
      let myVoid1: void = undefined;
      let myVoid2: void = null;
      
    5. 定义null和undefined
      // null
      let nullSet: null = null;
      // undefined
      let undeSet: undefined = undefined;
      
      需要注意的是,null和undefined可以被赋予任何类型,但是void的空值不能被赋予其他类型。
      let unsetNumber: number = null;
      let unsetString: string = undefined;
      
    6. 任意类型
      我们都非常熟悉JavaScript,在JavaScript中,我们的变量是可以随时转换数据类型的,但是在TypeScript中,对于定义好的基本数据类型是不可以的!那我们如何实现一个不一定是什么数据类型的值呢?就该使用任意类型了。
      // 任意类型
      // 对于任意类型,可以在赋值后修改数据类型。
      let myAny: any = 'string';
      myAny = 123;
      // 变量在声明的时候没有指定类型,并且没有赋值,自动编译为 any 类型
      let anyThing;
      anyThing = '未指定类型并且未赋值,自动编译为 any 类型';
      anyThing = 1234;
      

    类型推断

    了解了基本数据类型,接下来就应该先了解一下TypeScript的类型推断,在TypeScript中,如果你没有给(变量/数据)指定一个数据类型,分为两种情况,

    1. 一种是定义未赋值,就是我们在上边的任意类型中提到的,它会自动声明成为any类型。
    2. 而如果我们在定义(变量/数据)的时候,给它赋值,但是不给它指定类型的话,TypeScript就会进行类型推断。
      // 但是如果变量在声明是赋值了,但是没有指定类型
      // 经过类型推断,它应该是赋值时候的数据类型,即为string。
      let stringThing = '未指定类型,但是赋值为string';
      stringThing = 1234; // 报错
      
      在上边的案例中,在定义stringThing变量定义时,给它赋值成为string,经过TypeScript的类型推断后,它会被设置成为string数据类型,而当我们给它赋值成为number类型时,TypeScript就会进行报错。

    联合类型

    我们在实际开发中经常会遇到这样的问题,一个参数,它既有可能是string类型,又有可能是number类型,那这种情况,如果我们用TypeScript应该怎么定义呢?肯定不可能使用any吧,那样的话,我们还可以设置其他类型的值。所以,就出现了联合类型。

    // 联合类型(多个类型)
    let numString: string | number = '一开始是string';
    numString = 123456789; // 可以被赋值为number类型
    numString = true; // 报错,不可以是boolean类型。
    

    但是,针对联合类型,只能使用它们之间公共的方法,不能使用其中一个特有的方法。例如:

    // 由于number是没有length属性的,所以会报错
    function getLength(some: string | number): number {
         return some.length;
    }
    

    访问number和string的公共属性没有任何问题,不会报错。

    function getString(some: string | number): string {
        return some.toString();
    }
    

    联合类型会在被赋值的时候推断出一个类型,并监听除当前数据类型以外的错误。

    let myName: string | number;
    myName = '郝晨光';
    // 此时是字符串类型,所以调用length属性没有任何问题
    console.log(myName.length); // 3
    myName = 123;
    // 此时是number类型,没有lenght属性,所以会报错
    console.log(myName); // error TS2339: Property 'length' does not exist on type 'number'.
    

    对象的类型

    在ts中,通过接口(interface)来定义对象的类型;

    1. 接口定义了对象的值的类型,并且定义了对象的参数,多一个参数少一个参数都不可以。

      interface Person {
          name: string,
          age: number,
          sex: string
      }
      
      let person: Person = {
          name: '郝晨光',
          age: 24,
          sex: '男'
      };
      
    2. 当我们的对象有一些属性不是必须的时候,可以通过?来定义,?表示属性是可选的。

      interface People {
          name: string,
          age: number,
          sex?: string
      }
      
      let girl: People = {
          name: 'girl',
          age: 20
      };
      
    3. 当我们的对象有一些未知的属性时,可以通过[propName]来定义

      interface Student {
          name: string,
          age?: number,
          [propName: string]: string | number
      }
      let student: Student = {
          name: '学生',
          age: 20,
          className: 'A班'
      };
      
      1. 如果设置了propName的话,可选的值(带?的值),就必须是propName的指定值的子类型
      2. 例如any,所有类型都是它的子类型,或者通过联合类型来指定
      3. 在上边的案例中,如果我只给propName指定数据类型为string的话,TypeScript就会报错,因为age属性需要的是number类型的值。
    4. 我们还可以给对象设置只读的属性。

      // 只读属性
      // 通过 readonly 属性来设置属性是只读的
      interface Teacher {
          readonly id: number,
          name: string,
          age?: number,
          [propName: string]: any
      }
      let teacher: Teacher = {
          id: 123456,
          name: '郝晨光',
          age: 24,
          className: 'A班'
      };
      console.log(teacher.id);
      teacher.id = 1231546; // 报错,因为是只读的,不能修改
      

    数组的类型

    在TypeScript中定义数组的方式有很多

    1. 类型 + 方括号
      let arr1: number[] = [1, 2, 3];
      // 如果出现非number的值,就会报错
      arr1 =  [1, 2, '1', 3]; // 报错
      arr1.push(10); // 正常
      arr1.push('10'); // 报错
      
    2. 数组泛型
      let arr2: Array<number> = [1, 2, 3];
      arr2 = [1, 2, '1', 3]; // 报错;
      arr2.push(10); //正常
      arr2.push('10'); // 报错
      
    3. 使用接口interface定义
      interface NumberArray {
          [index: number]: number // 定义数据是number类型
      
          push(number: number): void; // 定义push方法接收number类型的值,并返回空值
      }
      let arr3: NumberArray = [1, 2, 3];
      arr3 = [1, 2, '1', 3]; // 报错;
      arr3.push(10); // 正常
      arr3.push('10'); // 报错
      
    4. 使用any定义数组
      // 数组中允许出现任意类型的值
      let arr4: any[] = [1, '2', new Date(), 3];
      
    5. 类数组
      类数组不能通过数组的方式定义,每一个类数组都有它对应的内置对象
      arguments ==> IArguments
      NodeList ==> NodeList
      HTMLCollection ==> HTMLCollection
      ···
      

    函数的类型

    在JavaScript中,定义函数有两种方式,一种是函数声明式,另一种是函数表达式。
    在函数定义之后,输入多余的(或者少于要求的)参数,是不被允许的。

    1. 函数声明式
      function fn(x: number, y: number): number {
          return x + y;
      }
      
    2. 函数表达式
      注意:这样声明对于左边的变量来说,并没有任何数据指定,虽然不会报错,但是是不严谨的
      let fn2 = function(x: number, y: number): number {
          return x + y;
      };
      
      所以我们应该这样定义。
      let fn3: (x: number, y: number) => number = function(x: number, y: number): number {
          return x + y;
      };
      
      需要注意的是TypeScript中,用在此处的 => 与 ES6中的箭头函数是没有任何关系的,此处只是为了指定返回值的数据类型。
    3. 使用接口interface定义函数
      interface Fun {
          (x: number, y: number): number
      }
      // 此时可以不指定右边的函数的返回值的类型,因为在接口中定义了
      let fn4: Fun = function(x: number, y: number) {
          return x + y;
      };
      
    4. 可选参数
      function fn5(firstName: string, lastName?: string): string {
          return firstName + ' ' + lastName;
      }
      
      需要注意的是,可选参数必须出现在最后,不能在可选参数之后又出现必须参数。
      下面的案例会报错!
      function fn6(x?: number,y: number): number {
          return x + y;
      }
      
      函数出现多个可选参数
      // 出现多个可选参数
      function fn7(x: number,y?: number, z?: number): number {
          return x + y + z;
      }
      
    5. 参数默认值
      当定义了默认值时,可选参数将不会在受限制,可以出现在必须参数前边,调用时直接传入null或者undefined即可。
      function fn8(x: number = 5,y: number):number {
          return x + y;
      }
      fn8(null,10); // 15
      fn8(10, 20); // 30
      
    6. 剩余参数
      在ES6中,可以通过扩展运算符来接收剩余的参数,剩余参数其实就是一个数组,所以可以通过any[]或者Array<any>等等方式定义类型。
      function fn9(arr: any[], ...args: any[]): Array<any> {
          args.forEach(item => {
              arr.push(item);
          });
          return arr;
      }
      
    7. 函数重载
      重载就是指函数在接收不同类型的参数或者不同数量的参数时,做出不同的处理,返回不同的结果。
      function reverse(x: number): number;
      function reverse(x: string): string;
      function reverse(x: number | string): number | string {
          if(typeof x === 'number') {
              return Number(x.toString().split('').reverse().join());
          }else if(x === 'string'){
              return x.split('').reverse().join('');
          }
      }
      reverse(123456);
      reverse('123456');
      

    本文参考:TypeScript入门教程 - 阮一峰

    如果本文对您有帮助,可以看看本人的其他文章:
    简单实现Vue响应式原理@郝晨光
    前端常见面试题(十六)@郝晨光
    前端常见面试题(十五)@郝晨光

    结言
    感谢您的查阅,本文由郝晨光整理并总结,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照

    相关文章

      网友评论

        本文标题:TypeScript入门学习@郝晨光

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