函数是 JavaScript 应用程序的基础,可以实现抽象层,模拟类,信息隐藏和模块。在 TypeScript 里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义行为的地方。TypeScript 为 JavaScript 函数添加了额外的功能,可以更容易地使用
基本示例
和 JavaScript 一样,TypeScript 也可以创建函数。
1 2 3 4 5 6 7 8 9
| function add(x, y) { return x + y; }
const myAdd = function (x, y) { return x + y; };
|
函数类型
定义函数类型
让我们为上面那个函数添加类型:
1 2 3 4 5 6 7
| function add(x: number, y: number): number { return x + y; }
const myAdd = function (x: number, y: number): number { return x + y; };
|
我们可以给每个参数添加类型之后再为函数本身添加返回值类型。TypeScript 能够根据返回语句自动推断出返回值类型。
完整函数类型
现在我们已经为函数指定了类型,下面让我们写出函数的完整类型。
1 2 3 4 5 6 7 8 9 10
| const myAdd2: (x: number, y: number) => number = function (x: number, y: number): number { return x + y; };
type addFnType = (x: number, y: number) => number; const myAdd2: addFnTyep = function (x: number, y: number): number { return x + y; };
|
参数相关
可选参数
JavaScript 里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是 undefined
。 在 TypeScript 里我们可以在参数名旁使用 ?
实现可选参数的功能
这个时候y
的类型其实是 undefined
和 number
类型的联合。
1 2 3 4 5 6 7
| function add(X: number, y?: number): number { if (y) { return x + y; } else { return x; } }
|
默认参数
在 TypeScript 里,也可以为参数提供一个默认值,当用户没有传递这个参数或传递的值是 undefined
时。 它们叫做有默认初始化值的参数
1 2 3
| function add(x: number, b: number = 6): number { return x + y; }
|
剩余参数
必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,想同时操作多个参数,或者并不知道会有多少参数传递进来。 在 JavaScript 里,你可以使用 arguments
来访问所有传入的参数。
在 TypeScript 里,你可以把所有参数收集到一个变量里:
剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号( ...
)后面给定的名字,你可以在函数体内使用这个数组。
1 2 3 4
| function info(x: string, ...args: string[]) { console.log(x, args); } info("abc", "c", "b", "a");
|
函数重载
函数重载: 函数名相同, 而形参不同的多个函数
在 JS 中, 由于弱类型的特点和形参与实参可以不匹配, 是没有函数重载这一说的 但在 TS 中, 与其它面向对象的语言(如 Java)就存在此语法
- 在 TypeScript 中,可以去编写不同的重载签名(overload signatures)来表示函数可以以不同的方式进行调用
- 一般是编写两个或者以上的重载签名,再去编写一个通用的函数以及实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
function add(arg1, arg2) { return arg1 + arg2; }
add(10, 20); add("abc", "cba"); add({ aaa: "aaa" }, 123);
function add1(num1: number, num2: number) { return num1 + num2; }
function add2(str1: string, str2: string) { return str1 + str2; }
add1(10, 20); add2("abc", "cba");
function add(arg1: number | string, arg2: number | string) { return arg1 + arg2; }
function add(arg1: number, arg2: number): number; function add(arg1: string, arg2: string): string;
function add(arg1: any, arg2: any): any { return arg1 + arg2; }
add(10, 20); add("aaa", "bbb");
|
需求:定义一个函数,可以传入字符串或者数组,获取它们的长度
两种实现方案
- 方案一:使用联合类型来实现
- 方案二:实现函数重载来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function getLength(arg) { return arg.length; }
function getLength(arg: string): number; function getLength(arg: any[]): number; function getLength(arg) { return arg.length; }
function getLength(arg: string | any[]) { return arg.length; }
function getLength(arg: { length: number }) { return arg.length; }
getLength("aaaaa"); getLength(["abc", "cba", "nba"]); getLength({ length: 100 });
|
调用签名
在 JavaScript 中,函数除了可以被调用,自己也是可以有属性值的
然而前面讲到的函数类型表达式并不能支持声明属性
如果想描述一个带有属性的函数,可以在一个对象类型中写一个调用签名(call signature
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| type BarType = (num: number) => number;
interface IBar { name: string; age: number; (num: number): number; }
const bar: IBar = (num: number): number => { return num; };
bar.name = "aaa"; bar.age = 18; bar(123);
|
构造签名
JavaScript 函数也可以使用 new
操作符调用,当被调用的时候,TypeScript 会认为这是一个构造函数(constructor
),因为 他们会产生一个新对象
- 可以写一个构造签名( Construct Signatures ),方法是在调用签名前面加一个
new
关键词
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Person { constructor() {} }
interface ICTORPerson { new (): Person; }
function factory(fn: ICTORPerson) { const f = new fn(); return f; }
factory(Person);
|
this 相关
函数中 this 默认类型
在tsconfig.json
设置了noImplicitThis
为true
时, TypeScript 会根据上下文推导this
,但是在不能正确推导时,就会报错,需要明确的指定this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
const obj = { name: "why", studying: function () { console.log(this.name.length, "studying"); } };
obj.studying();
function foo() { console.log(this); }
|
函数中 this 明确类型
在开启noImplicitThis
的情况下,必须指定this
的类型
函数的第一个参数类型
- 函数的第一个参数可以根据该函数之后被调用的情况,用于声明
this
的类型(名词必须叫this
)
- 在后续调用函数传入参数时,从第二个参数开始传递的,
this
参数会在编译后被抹除
1 2 3 4 5 6
| function foo(this: { name: string }, info: { name: string }) { console.log(this, info); }
foo.call({ name: "Tom" }, { name: "kobe" });
|
this 相关内置工具
Typescript 提供了一些工具类型来辅助进行常见的类型转换,这些类型全局可用
ThisParameterType
ThisParameterType
- 用于提取一个函数类型
Type
的this
(opens new window)参数类型
- 如果这个函数类型没有
this
参数返回unknown
1 2 3 4 5 6 7 8
| function foo(this: { name: string }, info: { name: string }) { console.log(this, info); }
type FooType = typeof foo;
type FooThisType = ThisParameterType<FooType>;
|
OmitThisParameter
OmitThisParameter
- 用于移除一个函数类型
Type
的this
参数类型, 并且返回当前的函数类型
1 2 3 4 5 6 7 8
| function foo(this: { name: string }, info: { name: string }) { console.log(this, info); }
type FooType = typeof foo;
type PureFooType = OmitThisParameter<FooType>;
|
ThisType
这个类型不返回一个转换过的类型,它被用作标记一个上下文的this
类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| interface IState { name: string; age: number; }
interface IStore { state: IState; eating: () => void; running: () => void; }
const store: IStore & ThisType<IState> = { state: { name: "aaa", age: 18 }, eating() { console.log(this.name); }, running() { console.log(this.name); } };
store.eating.call(store.state);
|