TypeScript 要点知识整理

3,255 阅读7分钟

本篇主要是整理一下 typescript 相关的知识点,实际的运用会在下篇文章仔细讲解。

什么是 TypeScript

TypeScript 是 JavaScript 的一个超级,主要提供了类型系统和对 ES6 的支持。

安装 TypeScript 及编译 Ts 文件

npm -g install typescrip // 全局安装 typescript

全局安装 typescript 后即可在终端使用 tsc 命令了,编译 ts 文件命令如下:

// hello.ts
function foo(name: string) {
    console.log(`Hello ${name}`);
}
foo('Cooper') // Hello Cooper

tsc hello.ts // 编译 ts 文件后生成对应的 hello.js 文件

TypeScript 的类型检查说明

Ts 只会进行静态类型检查,如果发现错误,编译的时候就会报错。日常一般搭配 Eslint 或 Tslint 使用,在编码过程中即会报出对应错误。

基本数据类型

通过在变量后加 :类型 声明变量的类型。

1. bolean 布尔值

let isSuccess: boolean = true; // 正确
let isSuccess: boolean = new Boolean(1); // 错误,new 产生的是一个Boolean类型的对象,类型为 Boolean,是一个构造函数,不同于基本数据类型 boolean
let isSuccess: Boolean = new Boolean(1); // 正确
let isSuccess: boolean = Bealean(1); // 返回的仍是一个基本boolean类型的值

说明: 在 typescript 中,boolean 是基本数据类型,而 Boolean 是一个构造函数,定义为 boolean 的值不能是 Boolean 对象。其他基本数据类型亦如此

2. 数字

let num: number = 3; // 正确
num = '3'; // 报错,申明了变量为 number 类型,怎么能赋 string 类型的值呀!

3. 字符串

let name: string = 'Cooper';
let templetStr: string = `Hello, ${name}`; // es6 模版字符串

4. 空值

JavaScript 中没有空值(Void)的概念,在 TypeScript 中空值一般用来表示一个函数没有返回值

// 定义一个没有返回值的返回的函数
function foo(name: string): void {
    console.log(name)
}

5. Null 和 Undefined

定义为 null 类型的变量只能被赋值为 null, 定义为 undefined 的变量只能被赋值为 undefined

let u: undefined = undefined;
let m: null = null;

任意值

定义为任意值的变量可以被赋值为任意类型。在任意类型的变量上访问任意属性和方法都是可以的。声明一个变量为 any 类型后,对其进行任意操作返回的都是 any 类型。未声明类型的变量默认为 any 类型。

let anyValue: any = '123';
anyValue = 1; // 不会报错,因为 anyValue 的类型为任意值

注意:any 用起来虽然特别方便,但是建议日常开发能不用就不用,毕竟 TypeScript 是为了更规范的书写 Js 代码,如果全部都定义成 any 类型,那就失去了 TypeScript 的意义。

类型推论

如果没有明确的指明某个变量的类型, 那么 TypeScript 会依照类型推论的规则推导出一个类型。

let str = 'Cooper';
str = 7; // 报错,根据类型推论确认 str 为 string 类型,则不可以赋 string 外的值

联合属性

联合类型表示取值可以为多种类型中的一种,各个类型间使用分隔线 | 连接

let strOrNumValue: string | number = 'test';
  • 访问联合类型的属性和方法 当一个变量定义为联合类型,我们只能访问联合类型的公共方法和属性
function getLength(something: string | number) {
    return something.length; // 报错,number 类型不包含 length 属性
}
function myToString(something: string | number) {
    return something.toString(); // 正确,number 和 string 类型都包含 toString 方法
}
  • 联合类型的变量在被赋值后,会根据类型推论的规则推导出一个类型
let something: string | number;
something = 'Cooper';
console.log(something.length) // 5, something 赋值字符串后,被推导为 string 类型,因而可以访问 length 属性
something = 3
console.log(something.length) // 报错

对象的类型 - 接口

在 TypeScript 中,我们用接口来定义对象的类型,接口命名,一般以大写字母 I 开头,且 I 后面的首个字母一并大写,了解 Java 的同学应该很熟悉,这与 Java 是一致的。

interface IPerson {
    name: string;
    age: number;
}
// 接口的所有属性都需要包含,且各个属性类型一致,否则报错
let Cooper: IPerson = {
    name: 'Cooper',
    age: 23
}
  • 可选属性
    用来表示某个对象中的属性是可选的。
interface IPerson {
    name: string;
    age?: number; // 我们在属性后使用 ? 后表示该属性为可选属性
}
// 可只包含 name 属性,age为可选属性,可不定义,但是不能添加接口未定义属性
let Cooper: IPerson = {
    name: 'Cooper'
}
  • 任意属性
interface IPerson {
    name: string;
    age?: string;
    [propName: string]: string;
}
let cooper: IPerson = { // true
    name: 'Cooper',
    gender: 'male' // 添加的任意属性 gender
}

注意:一旦定义了任意类型,那么确认属性和可选的属性的类型必须是任意属性类型的子类,举个例子,以上定义改为


// 报错,任意属性为 string,不包括可选属性的 number 类型
interface IPerson {
    name: string;
    age?: number;
    [propName: string]: string;
}
// 应改为以下形式
interface IPerson {
    name: string;
    age?: number;
    [propName: string]: string | number; // 上面定义的具体类型也应包含在任意类型
}
  • 只读属性 有时候我们希望有些属性只能在初始化时被赋值,后续不允许修改,则可以使用 readonly 关键字来定义
interface IPerson {
    readonly id: number;
    name: string
}
let cooper: IPerson = {
    id: 14202119,
    name: 'Cooper'
}
cooper.id = 14020120 // 报错,只读属性不允许再次修改值

数组的类型定义

在 TypeScript 中数组可以使用多种方式定义类型。

  • 类型 + 中括号
let list: number[] = [1, 2, 3]; // 数组中的每一项都 number 类型
  • 数组泛型,Array[T], T 为泛型
let list: Array<number> = [1, 2, 3];
  • 用接口表示
// key 为 number 类型,即数组下标,值为 number 类型,即 value
interface INumberArray {
    [index: number]: number
}
let list: INumberArray = [1, 2, 3]
  • 对象数组的定义
interface IItem = {
    name: string,
    age: number
}
// 数组中是一个对象,且对象中属性需和定义的一致
let list: IItem[] = [
  {
    name: 'Cooper',
    age: 23
  },
  {
    name: 'Bob',
    age: 23
  },
]

函数类型

  • 函数的声明方式
// 函数式声明
function foo(name) {
    console.log(name);
}
// 函数表达式
const mySun = function(x, y) {
    return x + y;
}
  • 函数式声明定义类型
// 接收两个 number 的参数,不允许少传或多传参数,且函数的返回值必须为 number 类型
function(x: number, y: number): number {
    return x + y;
}
  • 函数表达式定义类型
let myFun: (x: number, y: number) => number = function(x: number, y: number): number {
    return x + y;
}
// 左侧变量的类型可省略,等号左边会根据类型推论推断出来,即:
let myfun = function(x: number, y: number): number {
    return x + y;
}
  • 使用接口定义
interface IMyFun {
    (x: number, y: number): boolean
}
let myFun: IMyFun = function(x: number, y: number): boolean {
    return false;
}
  • 可选参数和剩余参数
// y 为可选参数
function myFun(x: number, y?: number): number {
    if (y) {
        return x + y;
    }
}
// 剩余参数
function myFun(x: number, ...ars: any[]) {
    console.log(args);
}
myFun(1, 2, '3'); // [2, '3']

类型断言

类型断言即手动(或者说是强制?。?)指定一个变量的类型

  • 语法
<类型>变量
变量 as 类型 // tsx 中仅支持此种方式
function myFun(x: number | string) {
    console.log(<string>x.length); // 此处变量 x 的属性为 string | number,想直接使用 length 属性,则需要通过类型断言指定 x 的属性为 string
}
  • 特殊
    当我们声明可选参数时,我们需要先判断参数存在才能使用
function myFun(x: number, y?: number): number {
    if (y) {
        const result = x + y; // 不进行判断将会报错
        return result;
    }
}
// 也可通过断言
function myFun(x: number, y?: number): number {
    const result = x + !y; // !表示改变量一定存在
    return result;
}

注意:类型断言不是类型转换,断言一个联合类型中不存在的类型将会报错。

声明文件

当我们直接在项目中引入第三方的模块,并且使用其方法或者是属性,ts 是不会报错的,因为这些类型和方法都是没有进行声明的。因为我们需要使用声明文件来定义这些属性和方法的类型。对于一些常用的第三方模块, typescript 社区已经帮我们定义好了,只要我们在使用 npm 安装模块的时候,在模块名前带上 @type/

npm install express --save// 安装普通的 express 模块
npm install @/types/express --save-dev// 安装带有声明文件的 express 模块,只有本地开发环境才进行安装

对于一些不常用的第三方模块,在 typescript 社区中无法安装带有声明文件的模块,我们可以在项目的根目录下创建一个 index.d.ts 文件,在其中对其进行声明,防止 ts 报错。

declare module 'so-utils'; // 声明 so-utils 为一个模块,防止 import * from 'so-utils' 报错

以上可防止模块引入时报错,但对于模块定义的属性和方法依旧没有提示,想要完整的代码提示功能则要定义完整的声明文件,关于声明文件的书写名,参见TypeScript 入门教程 - 声明文件

内置对象

JavaScript 中有很多定义好的对象,我们可以当作定义好的类型来使用。

let bodyDoc: HTMLDocument = document.body; // html文档流

类型别名

我们使用 type 关键字来声明类型的别名

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}

字符串字面量类型

字符串字面量类型用来约束取值只能是某几个字符串取值中一个

type eventName = 'click' |  'scroll' | 'mousemove'
// 第二个参数 event 只能是'click' |  'scroll' | 'mousemove'三个中的某一个
function handleEvent(ele: Element, event: EventNames) {
    // do something
}
handleEvent(doument.getELementById('hello'), 'scroll'); // true
handleEvent(doument.getELementById('hello'), 'dbclick'); // 报错

参考