学习记录-week3(TypeScript)

484 阅读6分钟

前言

本周的主要任务是过完 TypeScript 的基础知识。因为 TypeScript 不是我想要分享、学习的重点,我们仅仅是为了利用 TypeScript 来养成一个良好的编码习惯,所以过的会快一些。

另外, TypeScript 的基础知识网上有非常多的教程,想要深入学习的同学不妨找来看看。 在这里我推荐下这个系列的教程:TypeScript 入门教程

学习目标

  1. 简单的类型声明

类型声明

原始数据类型

原始数据类型有 7种 ,分别是:

  • boolean
  • null
  • undefined
  • number
  • string
  • symbol
  • bigint

忽略掉 ES6 中新增的 symbol 与 bigint 类型,其他的类型声明方法大致如下:

const name: string = '张三';

const age: number = 18;

const isMarried: boolean = false;

const u: undefined = undefined;

const n: null = null;

值得注意的是,使用 BooleanStringNumber 这类构造函数创建的对象,不能使用原始数据类型:

const b: boolean = new Boolean(false); // 报错

const b: Boolean = new Boolean(false); // 正确

但是最好不要将包装对象用作于构造函数

任意值

let anything: any = '18';

anything = 18;

被声明为任意值的变量在被赋值时可以被更改为任意类型的值,但是能不用 any 就不用 any ,否则类型检查的意义就不是很大了

接口

TypeScript 对象的接口可以用于对类的一部分行为进行抽象,也可以用于对对象的结构的描述

简单应用

一个简单的例子是:

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

const person: Person = {
  name: 'tom',
  age: 18,
};

赋值的时候,变量的形状必须和接口的形状保持一致。属性不能多,也不能少。

可选属性

如果希望属性可选,那么我们可以这么定义:

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

const person: Person = {
  name: 'tom',
  age: 18,
};

如此一来,sayHello 就是一个可选属性了。但是仍然不能添加未定义的属性

任意属性

当需要定一个一个未在接口中声明的属性时,我们可以使用这种形式:

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

const person: Person = {
  name: 'tom',
  age: 18,
  gender: 'male',
};

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

数组

数组拥有多种表示方法,非常灵活。

类型[方括号] 表示法

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

数组范型

数组范型Array<elemType>也可以用来表示数组:

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

函数

首先来复习下基础知识,函数有两种常见的定义方式。函数声明和函数表达式。

函数声明:

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

函数表达式:

const sum = function(x, y) {
  return x + y;
};

通用表示

用 TypeScript 对函数进行约束,肯定是既约束参数,又约束返回值

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

函数声明:

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

函数表达式:

const sum: (x: number, y: number) => number = (x: number, y: number): number => {
  return x + y;
};

在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。 上面的第一个 => 是 TypeScript 类型定义中的 => ,而第二个 => 则是 ES6 中的箭头函数。

可选参数

和接口类似,我们使用?来表示可选参数:

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

注意:可选参数必须接在必需参数后面

参数默认值

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

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

sum(undefined, 3); // 3

剩余参数

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

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

push([], 1, 2, 3); // [1, 2, 3]

注意:rest 参数只能是最后一个参数

重载

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

function reverse(x: number | string): number | string | undefined {
  if (typeof x === 'number') {
    return Number(x.toString().split('').reverse().join(''));
  } else if (typeof x === 'string') {
    return x.split('').reverse().join('');
  }
  return undefined;
}

这样有一个缺点,就是不能够精确的表达,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串

这时,我们可以使用重载定义多个 reverse 的函数类型:

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | undefined {
  if (typeof x === 'number') {
    return Number(x.toString().split('').reverse().join(''));
  } else if (typeof x === 'string') {
    return x.split('').reverse().join('');
  }
  return undefined;
}

上例中,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。

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

类型断言

类型断言可以手动的指定一个值的类型。

之前说过, TypeScript 不确定联合类型变量到底是哪个类型的时候,我们只能访问此联合类型中的所有类型共有的方法和属性:

interface Cat {
  name: string;
  run(): void;
}
interface Fish {
  name: string;
  swim(): void;
}

function getName(animal: Cat | Fish) {
  return animal.name;
}

而当我们访问某个类型特有的属性或方法时,就会报错:

function isFish(animal: Cat | Fish) {
  if (typeof animal.swim === 'function') {
    return true;
  }
  return false;
}

此时,我们可以将 animal 断言为 Fish:

function isFish(animal: Cat | Fish) {
  if (typeof (animal as Fish).swim === 'function') {
    return true;
  }
  return false;
}

类型断言还有很多其他的使用场景,但是核心就是:只有当你确定、确定、确定某个值的类型时,再用断言

结语

好了,本期的内容差不多就到这里了,如果是有其他静态语言经验的同学,学起来应该会比较快。还是我最开始说的那句话,我们学习 TypeScript 的目的是为了养成良好的编码习惯,而养成良好编码习惯的目的是写出更健壮的代码。本来是想花一周时间来过完 TypeScript 的简单使用了,但是目前来看,有的内容得放到下周了。就酱,溜了溜了。(PS:这周真的太忙了,希望下周不摸)