Ts函数

在 TypeScript 里定义函数时,我们可以显式指定函数参数和返回值的类型:

1
2
3
const add = (a: number, b: number): number => {
return a + b;
}

返回值类型
在 JavaScript 中,我们知道一个函数可以没有显式 return,此时函数的返回值应该是 undefined:

1
2
3
4
function func() {
// do sth
}
console.log(func()); // => undefined

需要注意的是,在 TypeScript 中,如果我们显式声明函数的返回值类型为 undfined,将会得到如下所示的错误提醒。

1
2
3
function func(): undefined { // 其声明类型不为 "void" 或 "any" 的函数必须返回值。ts(2355)
// do sth
}

此时,正确的做法是使用void 类型来表示函数没有返回值的类型,示例如下:

1
2
3
function funcA(): void {
}
funcA().doSomething(); // 类型“void”上不存在属性“doSomething”。ts(2339)

我们可以使用类似定义箭头函数的语法来表示函数类型的参数和返回值类型,此时=> 类型仅仅用来定义一个函数类型而不用实现这个函数。

需要注意的是,这里的=>与 ES6 中箭头函数的=>有所不同。TypeScript 函数类型中的=>用来表示函数的定义,其左侧是函数的参数类型,右侧是函数的返回值类型;而 ES6 中的=>是函数的实现。

如下示例中,我们定义了一个函数类型,并且使用箭头函数实现了这个类型。

1
2
type Adder = (a: number, b: number) => number; // TypeScript 函数类型定义
const add: Adder = (a, b) => a + b; // ES6 箭头函数

在对象中,除了使用这种声明语法,我们还可以使用类似对象属性的简写语法来声明函数类型的属性,如下代码所示:

1
2
3
4
5
6
7
8
9
10
interface Entity {
add: (m: number, n: number) => number;
del(m: number, n: number): number;
}
const entity: Entity = {
add: (m, n) => m + n,
del(m, n) {
return m - n;
},
};

可缺省和可推断的返回值类型
很多时候,我们不必或者不能显式地指明返回值的类型,这就涉及可缺省和可推断的返回值类型的讲解。
函数内是一个相对独立的上下文环境,我们可以根据入参对值加工计算,并返回新的值。从类型层面看,我们也可以通过类型推断加工计算入参的类型,并返回新的类型,示例如下:

1
2
3
4
5
6
7
8
function generateTypes(m: string, n: number) {
const numbers = [n];
const strings = [m]
return {
numbers,
strings
} // 返回 { numbers: number[]; strings: string[] } 的类型
}

参数类型

1
2
3
4
5
6
7
8
9
10
function log(x?: string) {
console.log(x);
}
function log1(x: string | undefined) {
console.log(x);
}
log();
log(undefined);
log1(); // 应有 1 个参数,但获得 0 个。ts(2554)
log1(undefined);

这里的 ?: 表示参数可以缺省、可以不传,也就是说调用函数时,我们可以不显式传入参数。但是,如果我们声明了参数类型为 xxx | undefined,就表示函数参数是不可缺省且类型必须是 xxx 或者 undfined。因此,在上述代码中,log1 函数如果不显示传入函数的参数,TypeScript 就会报一个 ts(2554) 的错误,即函数需要 1 个参数,但是我们只传入了 0 个参数。

this
众所周知,在 JavaScript 中,函数 this 的指向一直是一个令人头痛的问题。因为 this 的值需要等到函数被调用时才能被确定,更别说通过一些方法还可以改变 this 的指向。也就是说 this 的类型不固定,它取决于执行时的上下文。
但是,使用了 TypeScript 后,我们就不用担心这个问题了。通过指定 this 的类型(严格模式下,必须显式指定 this 的类型),当我们错误使用了 this,TypeScript 就会提示我们.
在 TypeScript 中,我们只需要在函数的第一个参数中声明 this 指代的对象(即函数被调用的方式)即可,比如最简单的作为对象的方法的 this 指向.

1
2
3
4
5
6
7
8
9
10
function say(this: Window, name: string) {
console.log(this.name);
}
window.say = say;
window.say('hi');
const obj = {
say
};
obj.say('hi'); // 类型为“{ say: (this: Window, name: string) => void; }”的 "this" 上下文不能分配给类型为“Window”的方法的 "this"。
类型“{ say: (this: Window, name: string) => void; }”缺少类型“Window”的以下属性: clientInformation, closed, customElements, devicePixelRatio 及其他 189 项。ts(2684)

在上述代码中,我们在 window 对象上增加 say 的属性为函数 say。那么调用window.say()时,this 指向即为 window 对象。
调用obj.say()后,此时 TypeScript 检测到 this 的指向不是 window,于是抛出了一个 ts(2684) 错误。
同样,定义对象的函数属性时,只要实际调用中 this 的指向与指定的 this 指向不同,TypeScript 就能发现 this 指向的错误,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
interface Person {
name: string;
say(this: Person): void;
}
const person: Person = {
name: 'captain',
say() {
console.log(this.name);
},
};
const fn = person.say;
fn(); //类型为“void”的 "this" 上下文不能分配给类型为“Person”的方法的 "this"。ts(2684)

我们要注意的是,显式注解函数中的 this 类型,它表面上占据了第一个形参的位置,但并不意味着函数真的多了一个参数,因为 TypeScript 转译为 JavaScript 后,“伪形参” this 会被抹掉,这算是 TypeScript 为数不多的特有语法。