typescrit学习笔记(1)基础

typescript学习(一)

官网的介绍为:TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。实际上就是规定了一种强类型的写法,加入了一个将ts代码编译为js代码的过程,可以在编译过程中及时发现错误,而不是等到运行时在页面上瞎鸡儿点,然后发现是变量类型错误,变量名写错这种糟心的bug。这种的Uncaught TypeError就很影响写代码时的心情。

安装与使用

TypeScript 的命令行工具安装方法如下:

npm install -g typescript

以上命令会在全局环境下安装 tsc命令,安装完成之后,我们就可以在任何地方执行 tsc命令了。

编译一个 TypeScript 文件很简单:

tsc hello.ts

我们约定使用 TypeScript 编写的文件以 .ts为后缀

优势与不足

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

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

TypeScript 非常包容

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

TypeScript 的缺点

任何事物都是有两面性的,我认为 TypeScript 的弊端在于:

  • 有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念
  • 短期可能会增加一些开发成本,毕竟要多写一些类型的定义,不过对于一个需要长期维护的项目,TypeScript 能够减少其维护成本
  • 集成到构建流程需要一些工作量
  • 可能和一些库结合的不是很完美

基本使用

JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。

原始数据类型包括:布尔值、数值、字符串、nullundefined以及 ES6 中的新类型 Symbol

基本类型

boolean

布尔值是最基础的数据类型,在 TypeScript 中,使用 boolean定义布尔值类型:

//编译通过
let isDone: boolean = false;
//使用构造函数生成的不是boolean值,而是一个对象
let createdByNewBoolean: boolean = new Boolean(1);
//可以使用Boolean方法返回一个boolean值
let createdByBoolean: boolean = Boolean(1);

数值

***

字符串

***

空值

JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void表示没有任何返回值的函数:

function alertName(): void {
    alert('My name is Tom');
}

声明一个 void类型的变量没有什么用,因为你只能将它赋值为 undefinednull

let unusable: void = undefined;

Null 和 Undefined

在 TypeScript 中,可以使用 nullundefined来定义这两个原始数据类型:

let u: undefined = undefined;
let n: null = null;

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

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

any类型

我们知道typescript是强类型的,因此是不允许在赋值过程中改变变量的类型,若变量的类型在程序运行过程中真的需要改变,那么我们可以将其定义为any类型

let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;   

对于未显式声明类型且未进行初始化的变量,它默认会被指定为any

let something;
something = 'seven';
something = 7;

对于未显式对于未显式声明类型但是进行过初始化的变量,会进行类型推断

let myFavoriteNumber = 'seven';
//事实上等同于
let myFavoriteNumber: string = 'seven';

联合类型

我们也可以对一个变量指定多个类型,在赋值时,ts也根据所赋值进行类型推断

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

并且我们可以访问这些类型的公共方法或者属性

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

Interfaces类型

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
};

在使用定义的接口类型时,性新对象内少一些或者多一些属性都是不被允许的,有时我们希望不要完全匹配一个接口,那么可以用可选属性

interface Person {
    name: string;
    age?: number;
}

let tom: Person = {
    name: 'Tom'
};

若我们需要添加接口内没有的属性,那么可使用任意属性

interface Person {
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    name: 'Tom',
    gender: 'male'
};

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

interface Person {
    name: string;
    age?: number;
    [propName: string]: string;
}

let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};

此时会报错,age会被先于任意属性做匹配,此时需要的是string类型,可以该为下面这种用法

interface Person {
    name: string;
    age?: number;
    [propName: string]: string | number;
}

let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};

只读属性

有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly定义只读属性:

interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    id: 89757,
    name: 'Tom',
    gender: 'male'
};

tom.id = 9527;

// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property

需要注意的是只读属性必须在给对象第一次赋值的时候进行设置

数组类型

简单用法

//数组内只允许存入数值
let fibonacci: number[] = [1, 1, 2, 3, 5];      

泛型用法

let fibonacci: Array<number> = [1, 1, 2, 3, 5];

接口用法(不推荐)

interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];

需要注意的是类数组不是数组,它是一个区别于普通数组的对象

函数类型

函数声明

function sum(x: number, y: number): number {
    return x + y;
}

函数表达式

let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

值得注意的是我们不止要对函数做出限制,也要对承载函数的那个变量做出限制,这里使用的=>与ES6中的=>是两码事。在 TypeScript 的类型定义中,=>用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

使用接口定义函数

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}

可选参数与默认值

function buildName(firstName: string='ccc', lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

需要注意的是:可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了

但是在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数:但是这种可选参数就不受可选参数必须接在必需参数后面约束了

function buildName(firstName: string = 'Tom', lastName: string) {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');