TypeScript入门学习@郝晨光


前言

现在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等等方式定义类型。
    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响应式原理@郝晨光
前端常见面试题(十六)@郝晨光
前端常见面试题(十五)@郝晨光

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

你可能感兴趣的:(javascript,TypeScript)