这篇博客记录function相关的更多生疏的知识
一、需要了解的特殊声明
在js中,我们知道方法是个对象,因此它自身是可以挂载属性的。但它还存在2个显著的特性:
- 可调用
- 可通过new关键字创建对象
这样的典型对象是Date。但typescript在使用function标签进行方法定义时,并没有给出相关的定义方式,例如如何在一个方法中定义属性,如何表示可以使用new关键字创建对象。因此针对对象,提供了2种特殊的定义方式。
1.构造标识
拥有构造标识的类型,表示可以使用new关键字。定义方式如下:
type SomeConstructor = {
// 构造标识
new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
return new ctor("hello");
}
2.调用标识
拥有调用标识的类型,表示可以被调用。定义方式如下:
type DescribableFunction = {
description: string;
// 调用标识
(someArg: number): boolean;
};
function doSomething(fn: DescribableFunction) {
console.log(fn.description + " returned " + fn(6));
}
上述定义方式,都是针对对象而言,因此也可以定义属性。但在日常开发中,极少会定义这种类型去用于去实现具体的类型,一般而言都是用于约束已经被定义好的APIs。
二、重载
js本来没有重载的说法,但typescript提供了重载的相关定义方式。方法重载一共包括如下2个部分:
-
重载声明部分
在使用typescript进行开发时,最主要的一个就是它提供了较为全面的代码提示。当调用一个重载方法时,则会有对应的重载提示,此时的提示列表则是根据这个声明部分来确定的。
-
方法实现部分
重载的方法都必须有一个实现,并且这个实现必须对前面的重载声明有一个较为全面的兼容,这个兼容包括入参类型以及返回类型。但这个实现方法本身并不在重载列表以内,下面列子说明。
function fn(x: string): void; function fn() { //具体实现 } //错误,应有 1 个参数,但获得 0 个 fn();在调用fn时,只有一个可选方法,那就是有入参的那个。具体的实现方法,不在可选列表内,因此会提示需要一个参数。
typescript的重载功能相对较为鸡肋,实质都需要一个方法来编写所有的兼容逻辑。因此使用重载前,谨慎考虑代码的复杂度和是否存在更简单的实现方案。
三、扩张参数(rest参数)
扩张参数有个特殊的语法,那便是...语法,当编写一个方法,没有确定具体的参数个数时,此时便可以这种不定参数的定义格式:
function multiply(n: number, ...m: number[]) {
return m.map((x) => n * x);
}
这种模式比较好理解,这儿不做过多阐述。假如存在一种情况,想使用这种方式传参给固定参数的方法,则会报错。比如:
const args = [8, 5];
const angle = Math.atan2(...args);
// 此时会报错,atan2方法接收2个参数,args数组是无法确定元素个数的
// 因此这儿提示了扩张参数必须具有元组类型或传递给 rest 参数
因此,这儿必须进行类型转换,官方提供了最为一种直接的方式,但具体情况需要具体分析,如下:
const args = [8, 5] as const;
//此时转换为了type const = readonly [8, 5],可以看到是固定的只读元组
const angle = Math.atan2(...args);
四、this类型
在js中,this对象的类型,需要结合具体的上下文来确定,是动态的,但在typescript中,使用的是静态分析的类型检查系统,是无法自动推断一个方法块中this对象的类型的,为了解决这个问题,typescript提供了一个特殊的this参数,用于指定方法中this对象的类型,定义如下:
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}
const db = getDB();
const admins = db.filterUsers(function (this: User) {
return this.admin;
});