函数
定义函数的方式
let 函数名 = function() {}
let mySum = function (x: number, y: number): number {
return x + y
}
function 函数名() {}
function mySum(x: number, y: number): number {
return x + y
}
限制函数类型的形状
直接限制
let ifExist: (str: string, val: string) => boolean = function(str: string, val: string) {
return str.indexOf(val) !== -1
}
间接限制
// 规定函数的形参类型跟返回值类型
// 写法1: 接口
interface SearchFn {
(source: string, target: string): boolean;
}
// 写法2: type定义类型别名
type SearchFn = {
(source: string, target: string): boolean;
}
let ifExist: SearchFn = function(str: string, val: string) {
return str.indexOf(val) !== -1
}
限制构造函数类型的形状
语法:
// 写法1: 接口
interface 接口名 {
new (构造函数形参: 形参类型): 该类型应包含实例对象的实例属性
}
// 写法2: type定义类型别名
type 类型名 = {
new (构造函数形参: 形参类型): 该类型应包含实例对象的实例属性
}
举例:
// 定义一个报警功能的接口
interface Alarm{
alarm(): void // 有一个报警方法
}
// 定义一个“构造函数类型”的接口,也可以用类型别名定义该类型
interface AlarmConstructor {
new (hour: number): Alarm // 限制生成的实例对象为Alarm类型
}
// 定义一个时钟,有报警功能
class Clock implements Alarm{
constructor(public h: number) {
this.h = h
}
// 定义时钟报警功能的具体实现
alarm() {
console.log("beep beep");
}
}
function clockClick(ctorArg: AlarmConstructor, hour: number): void {
// 由于AlarmConstructor接口类型的定义,保证生成的实例对象一定会有alarm方法
new ctorArg(hour).alarm()
}
clockClick(Clock, 1)
函数重载
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
案例
实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 'hello' 的时候,输出反转的字符串 'olleh'。
// 不使用重载:无法限制形参与返回值的类型相同,且只能是number | string类型
function reverse(x: number | string): number | string | void {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
// 使用重载:明确定义形参与返回值的类型,对不同的形参做出不同的处理
// 前几次都是函数定义,最后一次是函数实现。函数实现的变量类型要包含定义时的变量类型
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
泛型函数
function fn<T> (a: T): T {
return T
}
fn('abc') // 不指定泛型,通过类型推断,等同于fn<string>('abc')
fn<number>(2) // 指定泛型
案例: 生成长度为n的数组,数组的每一项都是value
function createArr<T>(length: number, value: T): Array<T> {
let arr: T[] = []
for (let i = 0; i < length; i++) {
arr[i] = value
}
return arr;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
泛型接口
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc = function<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
也可以把泛型参数提前到接口名上
interface CreateArrayFunc<T> {
(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc<any> = function<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
泛型约束
我们有时候想操作某类型的一组值,并且我们知道这组值具有什么样的属性。 在getLength例子中,我们想访问arg的length属性,但是编译器并不能证明每种类型都有length属性,所以就报错了。
function getLength<T>(arg: T): T {
console.log(arg.length); // Error: T doesn't have .length
return arg;
}
为此,我们定义一个接口来描述约束条件。 创建一个包含 .length属性的接口,使用这个接口和extends关键字来实现约束:
interface Lengthwise {
length: number;
}
function getLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 此时就能保证传入的参数一定有length属性,这里不会报错了
return arg;
}
现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:
loggingIdentity(3); // Error, number没有length这个属性
泛型参数的默认类型
在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。
当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}