likes
comments
collection
share

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

作者站长头像
站长
· 阅读数 2

数组-Array

数组在 TS 里面分成两种类型,分别是数组(Array)和元组(Tuple)。

两种基本写法

TS 中定义数组类型的方式常见的有两种方式。

第一种是以 T[] 形式如下:

let arrNum: number[] = [1, 2, 3];
let arrStr: string[] = ['a', 'b', 'c'];
let arrBool: boolean[] = [true, false];

第二种是以"泛型"的形式(泛型后续会讲):

let arrNum1: Array<number> = [1, 2, 3];
let arrStr1: Array<string> = ['a', 'b', 'c'];
let arrBool1: Array<boolean> = [true, false];

上面可以看到数组中所有成员的类型必须相同

🍊关于如何定义数组中成员类型不相同的情况?

可以使用"联合类型"来定义(联合类型后续会讲)。

let result1: (number | string | boolean)[] = [1, 'a', true];
let result2: Array<number | string | boolean> = [1, 'a', true];

或者可以定义成 any[] 类型。

多维数组

多维数组的定义方式推荐使用 T[][][]... 形式,泛型的形式有点像套娃,可读性略差。

let arr1: number[][] = [[1], [2], [3]]; 
let arr2: number[][][] = [[[1]], [[2]], [[3]]];

let arr11: Array<Array<number>> = [[1], [2], [3]];
let arr22: Array<Array<Array<number>>> = [[[1]], [[2]], [[3]]];

函数-Function

JS 中有三种函数,分别是命名函数、匿名函数、箭头函数,分别如下:

function sum(a, b) { return a + b }
let each = function(...args) {};
let getName = (name = '橙某人') => name;

那么,它们在 TS 中要如何定义呢?

三种基本写法

function sum(a: number, b: number): number { return a + b }
let each = function(...args: any[]): void {};
let getName = (name = '橙某人'): string => name;

🍊关于如何定义任何函数类型?

可以使 Function 类型。

let fn1: Function = () => {};
let fn2: Function = (a: string) => {};
let fn3: Function = function fn() {};

可选参数

JS 中,函数参数默认都是可选的,传不传取决于函数具体的业务逻辑。而在 TS 中,函数参数默认都是必填的,定义了参数但是不传就会有报错。

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

如果你想将参数变成可选的,需要使用 ? 符号来明确定义。

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

🍊关于如何用除了 ? 符号还能怎样将函数参数变成可选的?

可以使用默认值。

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

默认值

TS 函数的默认值与 JS 是一致的,但 TS 的默认值有两个好处。

  • 函数默认值能自动推断参数类型。
  • 函数默认值能将参数变成可选的。

还有,还有,还有一点要注意的❗函数默认值和可选参数不能同时使用

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

rest

rest 参数的类型可以是数组或者元组类型,一样必须放到参数最后一位。

function fn1(a: number, ...args: number[]) {}

function fn2(a: number, ...args: [number, string, boolean]) {}

返回值

函数返回值的类型定义是直接在括号(():T)加上类型即可。

function fn(): T {}

值得一提的是返回值类型也能自动进行类型推断。

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

函数重载

函数重载TS 自己扩展的一个概念,注意 JS 是没有的。

我们先不去探究它的概念情况,来看一个例子。

"有一个数组,我们期待实现一个函数,该函数根据不同数量的参数对数组有不同行为的表现。"

JS 中的实现:

const target = [1, 2, 3];

function fn(...args) {
  if (args.length === 0) {
    // 没有参数的时候,直接打印
    target.forEach(val => {
      console.log(val);
    })
  } else if (args.length === 1) {
    // 只有一个参数的时候,通过索引找到值
    return target[args[0]];
  }else {
    // 多个餐护士的时候,直接返回数组
    return target;
  }
}

而改成 TS 形式,也仅需将参数和返回值类型加上即可,如下:

const target = [1, 2, 3];

function fn(...args: any[]): void | number | number[] {
  if (args.length === 0) {
    target.forEach(val => {
      console.log(val);
    })
  } else if (args.length === 1) {
    return target[args[0]];
  }else {
    return target;
  }
}

不过,你以为如此就完了吗?那...当然,也是可以就这样就完了的。😇

不过铁子也可以继续听小编扯一扯。👻

此时,如果你去调用该函数,函数返回结果的类型永远都会是 number | void | number[]

const result1 = fn(); // number | void | number[]
const result2 = fn(1); // number | void | number[]
const result3 = fn(1, 2); // number | void | number[]
const result4 = fn(1, 2, 3); // number | void | number[]

然后呢❓❓❓这有什么问题吗❓

当然有,拿 result2 来说,其实我们能机构它的类型应该是 number 类型。此时,我们还将期待 result2 能通过 toFiexd() 方法,保留两位小数。

我们可能会如下调用 toFiexd() 方法:

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

但事与愿违,这样是不被允许。

而为了解决这种情况,就轮到"函数重载"隆重登场了,它规定必须给予函数完整的类型声明,才能准确得到期待的类型。

function fn(): void
function fn(index: number): number
function fn(...args: any[]): number[]
function fn(...args: any[]): void | number | number[] {
  if (args.length === 0) {
    target.forEach(val => {
      console.log(val);
    })
  } else if (args.length === 1) {
    return target[args[0]];
  }else {
    return target;
  }
}

const result1 = fn(); // void
const result2 = fn(1).toFixed(2); // number
const result3 = fn(1, 2); // number[]
const result4 = fn(1, 2, 3); // number[]

这样你可明白它的作用?😋其实也没那么高大尚,不过学过 Java 的小伙伴可能对此更熟悉。

函数重载:指的是根据函数参数不同的类型或数量,执行不同逻辑的行为,称为函数重载(function overload)。

对象-Object

要声明一个对象类型,我们需要列出它的所有属性及类型。

let obj: { name: string, age: number } = { name: '橙某人', age: 18 };

一旦声明了类型,对象赋值时,就不能缺少指定的属性,也不能有多余的属性。

let obj1: { name: string, age: number } = { name: '橙某人' }; // ❌
let obj2: { name: string } = { name: '橙某人', age: 18 }; // ❌

但是,由于 TS 不区分对象自身的属性和继承的属性,一律视为对象的属性,所有,有些通过原型链隐式继承下来的属性,在赋值的时候,咱们也可以不用管。

比如:

let obj1: { name: string, toString: Function } = { name: '橙某人' }; // ✅
let obj2: { name: string, valueOf: Function } = { name: '橙某人' }; // ✅

可选参数

对象类型也可以通过 ? 符号指定部分或者全部属性是可选的。

let obj: { name: string, age?: number } = { name: '橙某人' };

解构赋值

对象解构赋值的类型定义有两种形式。

let { name, age }: { name: string, age: number } = { name: '橙某人', age: 18 };

// or

let { name, age } = { name: '橙某人', age: 18 }; // 类型推断

注意❗一定不能写成这样子:

let { name: string, age: number } = { name: '橙某人', age: 18 }; // ❌

这样表示多定义了两个变量 let string;let number;

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

目前还无法为解构变量指定类型,因为对象解构里面的冒号,JS 指定了其他用途。

结构类型原则

先来看个例子:

let person: { name: string } = { name: '人类' };
let student: { name: string, age: number } = { name: '橙某人', age: 18 };

person = student; // ✅
student = person; // ❌

看这个结果是不是有点像包含、继承的意味?

只要 student 类型满足 person 类型的结构特征,TS 就会认为 student 类型是其子类型,person 是父类型。凡是可以使用父类型的地方,都可以使用子类型。

这种特性被称之为"结构类型"(structual typing)原则。


至此,本篇文章就写完啦,撒花撒花。

🚀🚀🚀Typescript通关秘籍(二)🔥🔥🔥

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。 老样子,点赞+评论=你会了,收藏=你精通了。