跳到主要内容

函数

函数类型表达式

function greeter(fn: (a: string) => void) {
fn('Hello, World');
}

调用签名

定义一个带属性的函数:

type DescribableFunction = {
description: string;
(someArg: number): boolean; // 注意是 `:` 而不是 `=>`
};

function doSomething(fn: DescribableFunction) {
console.log(fn.description + ' returned ' + fn(6));
}

构造签名

type SomeConstructor = {
new (s: string): SomeObject;
};

function fn(ctor: SomeConstructor) {
return new ctor('hello');
}

如果既是构造函数,又是普通函数:

interface CallOrConstruct {
new (s: string): Date;
(n?: number): number;
}

泛型函数

// 能自动推断返回值类型为 `Type[]`
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2);
}

可以进行类型约束:

function longest<Type extends { length: number }>(a: Type, b: Type) {
return a.length >= b.length ? a : b;
}

类型约束中,注意返回值类型的标明:

function minimumLength<Type extends { length: number }>(
obj: Type,
minimum: number,
): Type {
if (obj.length >= minimum) {
return obj;
} else {
return { length: minimum } as Type;
}
}

如何使用好泛型函数

类型下放

// Good
function fn1<Type>(arr: Type[]) {}

// Bad
function fn2<Type extends any[]>(arr: Type) {}

避免冗余的类型参数

// Good
function filter1<Type>(arr: Type[], fn: (arg: Type) => boolean): Type[] {
return arr.filter(fn);
}

// Bad
function filter2<Type, Fn extends (arg: Type) => boolean>(
arr: Type[],
fn: Fn,
): Type[] {
return arr.filter(fn);
}

可选参数

? 或者设默认值来使一个函数参数可选:

// 以下 `x` 实际的类型均为 `number | undefined`
function fn1(x?: number) {}
function fn2(x = 10) {}

函数重载

你可以对一个函数写多个重载签名,同时必须有一个实现签名,且兼容这些重载签名:

// 重载签名
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
// 实现签名(需自行判断重载)
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}

const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3); // Error: 没有两个参数的重载

重载中同一个位置的参数可以有不同类型(返回值也可以不同):

function isFalsy(num: number): boolean;
function isFalsy(str: string): boolean;
function isFalsy(value: number | string) {
if (typeof value === 'number') {
return value === 0 || isNaN(value);
} else {
return value === '';
}
}

const b = isFalsy('hello'); // => false

写函数时,应当考虑是否需要用到重载。比如上面的例子,其实可以完全去除重载签名。

函数重载是灵活的语法,只要实现签名能兼容所有重载签名,重载签名都是合法的。

函数中使用 this

在函数声明中需要将 this 作为第一个参数,才能使用 this 值:

interface Person {
name: string;
}

function greet(this: Person) {
console.log(`Hello, I'm ${this.name}.`);
}

// 实际调用时不需要传入 `this` 参数
greet.call({ name: 'Talaxy' }); // => "Hello, I'm Talaxy."

注意:只能在函数声明或函数表达式中使用 this ,箭头函数不可能有 this 值。

可以将 this 设为 void 来禁止在函数中使用 this

interface Person {
name: string;
greet: (this: void) => void;
}

const person: Person = {
name: 'Talaxy',
greet() {
// ERROR: `void` 类型不存在 `name` 属性
console.log(`Hello, I'm ${this.name}.`);
},
};

其他需要了解的类型

void

void 作为函数返回值类型时( TS 的视角)会忽略函数体中的任何返回:

type VoidFn = () => void;

const fn: VoidFn = () => {
return 'hello';
};

fn().length; // ERROR: `void` 类型不存在 `length` 属性

但在实际执行中 fn() 依旧会返回 "hello" 。

在函数中,若没有任何明确值返回,TS 会将返回值推断为 void ;如果在函数定义时将返回值设为 void ,则函数体里禁止任何明确值返回。

const fn = (): void => {
return 10; // ERROR: 不能将 `number` 赋值给 `void` 类型
};

object

object 代表一个非原始值,即对象,但与全局类型 Object 不同。

在 TS 中,你可能永远不会将 Object 作为类型。

unknown

any 类似,unknown 代表任何值,但是意为“未知”,表示不能用 unknown 值做任何事:

const a: any = 12.34;
const b: unknown = 12.34;
a.toFixed(); // => "12"
b.toFixed(); // ERROR: `a` 是个未知类型

unknown 作为函数返回值类型:

function safeParse(s: string): unknown {
return JSON.parse(s); // `JSON.parse` 的返回值为 `any`
}

// 需要对 `obj` 做类型转换才可以合法使用
const obj = safeParse(someRandomString);

never

never 代表不可能返回一个值,比如函数抛出了异常或终止了程序。

此外,never 还可以代表函数体中的类型边界(给 TS 自己用的,正常人用不到 😅 ):

function fn(x: string | number) {
if (typeof x === 'string') {
// ...
} else if (typeof x === 'number') {
// ...
} else {
x; // x 的类型为 'never'
}
}

Function

与 JS 中的 Function 一致。

Function 类型允许传入任意参数,返回值始终为 any 。最好不要用这个类型。

剩余参数

剩余形参

剩余形参的类型应当为一个数组( Array<T>T[] ):

function multiply(n: number, ...m: number[]) {
return m.map((x) => n * x);
}

剩余实参

可以通过数组展开语法传入实参:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);

对于无剩余形参的函数应当使用元组(而非一般数组):

const args: [number, number] = [8, 5];
const angle = Math.atan2(...args);

参考

More on Functions - TypeScript