教程5:函数+泛型

181 阅读4分钟

函数

定义函数的方式

  1. let 函数名 = function() {}
let mySum = function (x: number, y: number): number {
    return x + y
}
  1. 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例子中,我们想访问arglength属性,但是编译器并不能证明每种类型都有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;
}