Leecason

vuePress-theme-reco Leecason    2018 - 2020
Leecason Leecason

Choose mode

  • dark
  • auto
  • light
主页
分类
  • CSS
  • FrontEnd
  • GraphQL
  • JavaScript
  • TypeScript
  • Vue
  • Webpack
  • 其它
  • 数据结构与算法
  • 浏览器相关
标签
时间线
GitHub
author-avatar

Leecason

80

Article

61

Tag

主页
分类
  • CSS
  • FrontEnd
  • GraphQL
  • JavaScript
  • TypeScript
  • Vue
  • Webpack
  • 其它
  • 数据结构与算法
  • 浏览器相关
标签
时间线
GitHub

TypeScript 基础使用

vuePress-theme-reco Leecason    2018 - 2020

TypeScript 基础使用

Leecason 2020-02-15

TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。

本文介绍了 TypeScript 中的常用类型和一些基本概念,对 TypeScript 有个初步的理解。

# 原始数据类型

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

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

# 布尔值

使用 boolean 定义布尔值类型

let isBoolean: boolean = false;
// 注意,使用构造函数 Boolean 创造的对象不是布尔值,返回的是 Boolean 对象
let createdByNewBoolean: Boolean = new Boolean(1);
// 直接调用 Boolean 返回的是布尔值类型
let createdByBoolean: boolean = Boolean(1);
1
2
3
4
5

# 数值

使用 number 定义数值类型

let decLiteral: number = 6;
let hexLiteral: number = 0xf00d; // 十六进制
let binaryLiteral: number = 0b1010; // ES6 中的二进制
let octalLiteral: number = 0o744; // ES6 中的八进制
let notANumber: number = NaN; // not a number
let infinityNumber: number = Infinity;
1
2
3
4
5
6

# 字符串类型

使用 string 定义字符串类型

let name: string = 'Tom';
let sentence: string = `my name is ${name}`; // 模板字符串
1
2

# 空值

JavaScript 没有空值(void)概念,在 TypeScript 中,用 void 表示没有返回值的函数。

function alertText (): void {
  alert('This is a text');
}

let unusable: void = undefined; // void 类型的只能将它赋值为 undefined 和 null
1
2
3
4
5

# Null 和 Undefined

let u: undefined = undefined; // undefined 类型只能被赋值为 undefined
let n: null = null; // null 类型只能被赋值为 null
1
2

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

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

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

let u: void;
let num: number = u;
// Type 'void' is not assignable to type 'number'.
1
2
3
4
5
6
7
8
9

# 任意值

任意值(Any)用来表示允许赋值为任意类型。 声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。

let anything: any = 'hello';
let anything: any = 123;
let anything: any = true;
let anything: any = null;
let anything: any = undefined;

// 如果在声明的时候,未指定其类型,那么它会被识别为任意值类型
let something;
something = 'seven';
something = 7;
1
2
3
4
5
6
7
8
9
10

# 类型推论

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

let myFavoriteNumber = 'seven';
// 等价于
let myFavoriteNumber: string = 'seven';
1
2
3

# 联合类型

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

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

function getLength(something: string | number): number {
  return something.length; // length 不是 string 和 number 的共有属性,所以会报错
}

function getString(something: string | number): string {
    return something.toString(); // 都有toString方法,访问 string 和 number 的共有属性是没问题的
}
1
2
3
4
5
6
7
8
9
10
11

# 对象的类型——接口

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

# 简单的例子

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

let tom: Person = {
  name: 'Tom',
  age: 25
};
1
2
3
4
5
6
7
8
9

接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀 。

定义的变量比接口少了一些属性是不允许的,多一些属性也是不允许的。

# 可选属性

有时我们希望不要完全匹配一个形状,那么可以用可选属性,这时仍然不允许添加未定义的属性。

interface Person {
  name: string;
  age?: number; // 可选属性
}
1
2
3
4

# 任意属性

如果希望一个接口允许有任意的属性,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

interface Person {
  name: string;
  age?: number;
  [propName: string]: any; // 任意属性
}
1
2
3
4
5

# 只读属性

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

只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候

interface Person {
  readonly id: number; // 只读属性
  name: string;
  age?: number;
  [propName: string]: any;
}
1
2
3
4
5
6

# 数组的类型

在 TypeScript 中,数组类型有多种定义方式,比较灵活。

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

// 也可以使用数组泛型(Array Generic) Array<elemType> 来表示数组
let fibonacci: Array<number> = [1, 1, 2, 3, 5];

// 使用接口
interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
1
2
3
4
5
6
7
8
9
10

类数组(Array-like Object)不是数组类型,比如 arguments,arguments 实际上是一个类数组,不能用普通的数组的方式来描述,而应该用接口。

类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等。

# 函数的类型

在 JavaScript 中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression)

// 函数声明(Function Declaration)
function sum(x, y) {
    return x + y;
}

// 函数表达式(Function Expression)
let mySum = function (x, y) {
    return x + y;
};
1
2
3
4
5
6
7
8
9

# 函数声明

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

输入多余的(或者少于要求的)参数,是不被允许的。

# 函数表达式

如果不给等号左边的 mySum 手动添加类型,则是通过赋值操作进行类型推论而推断出来的。

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

在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

在 ES6 中,=> 叫做箭头函数,应用十分广泛。

# 接口定义函数的形状

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

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}
1
2
3
4
5
6
7
8

# 可选参数

与接口中的可选属性类似,我们用 ? 表示可选的参数。

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

function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
1
2
3
4
5
6
7
8
9

# 参数默认值

TypeScript 会将添加了默认值的参数识别为可选参数,此时就不受「可选参数必须接在必需参数后面」的限制了。

function buildName(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
1
2
3
4
5

# 剩余参数

ES6 中,可以使用 ...rest 的方式获取函数中的剩余参数(rest 参数)。

事实上,剩余参数是一个数组。所以我们可以用数组的类型来定义它。

rest 参数只能是最后一个参数

function push(array: any[], ...items: any[]) {
    items.forEach(function(item) {
        array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);
1
2
3
4
5
6
7
8

# 重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。

比如,我们需要实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 'hello' 的时候,输出反转的字符串 'olleh'。输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串。

// 可以使用重载定义多个 reverse 的函数类型
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 (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}
1
2
3
4
5
6
7
8
9
10

我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。

TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

# 类型断言

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

<类型>值 或者 值 as 类型

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法,此时可以使用类型断言。在需要断言的变量前加上 <Type> 即可。

function getLength(something: string | number): number {
  if ((<string>something).length) {
      return (<string>something).length;
  } else {
      return something.toString().length;
  }
}
1
2
3
4
5
6
7

# 声明文件

当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

  • declare var 声明全局变量
  • declare function 声明全局方法
  • declare class 声明全局类
  • declare enum 声明全局枚举类型
  • declare namespace 声明(含有子属性的)全局对象
  • interface 和 type 声明全局类型
  • export 导出变量
  • export namespace 导出(含有子属性的)对象
  • export default ES6 默认导出
  • export = commonjs 导出模块
  • export as namespace UMD 库声明全局变量
  • declare global 扩展全局变量
  • declare module 扩展模块
  • /// <reference /> 三斜线指令

# 内置对象

内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。

# ECMAScript 的内置对象

Boolean、Error、Date、RegExp 等。

let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
1
2
3
4

# DOM 和 BOM 的内置对象

Document、HTMLElement、Event、NodeList 等。

let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
  // Do something
});
1
2
3
4
5