No more than code.
Typescript 函数 / 声明文件
| 函数的类型
| 定义函数
用接口定义函数的形状
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
使用类型别名
type Add = (x: number, y: number) => number;
let add: Add = (arg1: string, arg2: string): string => arg1 + arg2; // error
完整的函数类型
// function add(arg1: number, arg2: number): number {
// return x + y;
// }
// const add = (arg1: number, arg2: number): number => {
// return x + y;
// };
let mySum = function (x: number, y: number): number {
return x + y;
};
上面的代码只对 = 右侧的匿名函数进行了类型定义,而等号左边的 mySum,是通过赋值操作进行类型推论而推断出来的。如果手动给 mySum 添加类型,则应该是这样:
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
// 同价于
let mySum: (x: number, y: number) => number;
mySum = function (x: number, y: number): number {
return x + y;
};
注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>。
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。 在 ES6 中,=> 叫做箭头函数。
| 参数
| 可选参数
前面提到,输入多余的(或者少于要求的)参数,是不允许的。那么如何定义可选的参数呢?
与接口中的可选属性类似,我们用 ? 表示可选的参数:
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必须参数了。
| 参数默认值
在 ES6 中允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数,此时就不受「可选参数必须接在必需参数后面」的限制了。
function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
| 剩余参数
const handleData = (arg1: number, ...args: number[]) => {};
handleData(1, "a"); // error 类型"string"的参数不能赋给类型"number"的参数
| 重载
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
比如实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 ‘hello’ 的时候,输出反转的字符串 ‘olleh’。
利用联合类型,可以这么实现:
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
然而这样有一个缺点,就是不能够精确的表达,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串。
这时,我们可以使用重载定义多个 reverse 的函数类型:
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
上例中,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。
注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
重载只能用 function 来定义,不能使用接口、类型别名等
| 声明文件
通常我们会把类型声明放到一个单独的文件中,这就是声明文件:
// jQuery.d.ts
declare var jQuery: (string) => any;
我们约定声明文件以 .d.ts 为后缀。
然后在使用到的文件的开头,用「三斜线指令」表示引用了声明文件:
/// <reference path="./jQuery.d.ts" />
jQuery('#foo');
第三方声明文件
社区已经有多种方式引入声明文件,不过 TypeScript 2.0 推荐使用 @types 来管理。
@types 的使用方式很简单,直接用 npm 安装对应的声明模块即可,以 jQuery 举例:
npm install @types/jquery --save-dev