跳到主要内容

函数

函数的类型就是描述函数入参类型与函数返回值类型

函数的类型签名

函数声明:

function foo(name: string): number {
return name.length;
}

const bar = function (name: string): number {
return name.length;
};

1、使用 type 声明:

type FnProps = {
(a: string): string;
};

type FnProps = (a: string) => string;

const fn: FnProps = a => a;

2、使用 interface 声明:

interface FnProps {
a: () => string;
}

interface FnProps {
a(): string;
}

在 interface 中,推荐按照foo(): string;的方式来定义方法,将属性和方法分开,因为它更直观、与类的方法声明方式一致。

interface Props {
foo(): string;
bar: () => void;
}

const obj: Props = {
foo() {
return 'hello';
},
bar: () => {}
};

参数注解

1. 内联类型注解

function fn1(a: string) {}

const fn2 = (a: string) => {};

const fn3: (a: string) => void = a => {};

type infoProps = (a: string) => void;
const fn4: infoProps = a => {};

2. 接口类型注解

interface Info {
name: string;
age: number;
}
function fn(prop: Info) {
return prop.name + prop.age;
}
fn({ name: 'zgh', age: 18 });

3. 可选参数

可选参数必须在必需参数后面

function fn(a: string, b?: string) {}

b 可以为 undefined,不能为 null

也可以将参数设置默认值,这样也是可选参数

4. 参数设置默认值

function fn(name: string = 'zgh') {}

参数不能有问号和初始值设定项

5. rest 参数

使用any[]或者元祖类型

function foo(arg1: string, ...rest: any[]) {}

function bar(arg1: string, ...rest: [number, string]) {}
bar('a', 1, 'b');

返回值注解

通常不需要给函数返回值添加注解,编译器会推断出来

// 没有返回值
function getInfo(): void {
console.log('This is message');
}

// 返回number
function getUser(a: number, b: number): number {
return a + b;
}

关于 void 类型:

// 当返回值是void时,不校验返回值
type FnProps = (a: string) => void;

const fn: FnProps = a => {
// 这里即使返回了值,也不会报错
return 1;
};

// 这样会报错
function gn(a: string): void {
return 1;
}

异步函数:Promise<T>

async function fn(): Promise<number> {
return new Promise((resolve, reject) => {
resolve(1);
});
}

函数重载

函数根据传入不同的参数而返回不同类型的数据。类型重载可以更好地表达函数的行为,在调用时提供更精确的类型检查。

定义函数的类型重载:

  1. 在函数实现之前,先声明多个函数签名
  2. 在最后一个签名之后,提供函数的具体实现

编译器会先从重载列表的第一个重载定义开始查找匹配项,直到选择出正确的检查类型。因此,在定义重载的时候,一定要把最精确的定义放在最前面。

假设有一个函数 f,如果传入参数是 string 类型,就返回 string 类型;如果是 number 类型,就返回 number 类型。利用联合类型,可以实现:

function f(x: string | number): string | number {
if (typeof x === 'string') {
return x;
} else if (typeof x === 'number') {
return x + 1;
}
return 0;
}

let a = f(1); // string | number

这样有一个缺点,就是定义不够精确,传入什么类型,输出也该是什么类型。如果只看function f(x: string | number): string | number {}这部分,我们无法知道f函数返回的是string还是number。这时就需要用到函数重载了。

function f(x: string): string;
function f(x: number): number;
function f(x: string | number): string | number {
if (typeof x === 'string') {
return x;
} else if (typeof x === 'number') {
return x + 1;
}
return 0;
}
let a = f(1); // number

这样改变后,重载的 f 函数在调用的时候会进行正确的类型检查。

函数签名和函数实现逻辑之间可以有空格,但是不能有其他代码。

function f(x: string): string;
function f(x: number): number;

// 不允许:let a = 1
function f(x: string | number): string | number {}

示例:有一个 format 函数可以格式化日期或数字

// 重载签名
function format(date: Date): string;
function format(number: number, precision: number): string;

// 函数实现
function format(value: Date | number, precision?: number): string {
if (value instanceof Date) {
return value.toDateString();
} else if (typeof value === 'number' && typeof precision === 'number') {
return value.toFixed(precision);
}
throw new Error('Invalid arguments');
}

const dateStr = format(new Date()); // 返回日期字符串
const numStr = format(3.14159, 7); // 返回 "3.1415900"