[TypeScript] 编程实践之1: Google的TypeScript代码风格6:函数

6 函数

TypeScript扩展了JavaScript函数,以包括类型参数,参数和返回类型注释,重载,默认参数值和rest参数。

6.1 函数声明

扩展了函数声明,以允许在重载声明中省略函数主体。

FunctionDeclaration: ( Modified )
	function BindingIdentifieropt CallSignature { FunctionBody }
	function BindingIdentifieropt CallSignature ;

FunctionDeclaration在包含的声明空间中引入函数类型的命名值。仅当函数声明出现在导出默认声明中时,BindingIdentifier才是可选的(11.3.4.2节)。

指定主体的函数声明称为函数实现,而没有主体的函数声明称为函数重载。可以为同一个函数指定多个重载(即,为同一声明空间中的同一个名称),但是一个函数最多可以有一个实现。同一函数的所有声明都必须指定相同的修饰符集(即,声明,导出和默认值的相同组合)。

当函数具有重载声明时,重载将确定给函数对象提供的类型的调用签名,并且函数实现签名(如果有)必须可分配给该类型。否则,函数实现本身将确定调用签名。

当一个函数同时具有重载和实现时,重载必须在实现之前,并且所有声明必须是连续的,并且中间没有语法元素。

6.2 函数重载

与单个签名相比,函数重载可以更准确地指定函数支持的调用模式。 对重载函数的调用的编译时处理为特定参数选择最佳候选重载,并且该重载的返回类型成为函数调用表达式的结果类型。 因此,使用重载可以静态描述函数的返回类型根据其参数而变化的方式。 函数调用中的重载解析在第4.15节中进一步描述。

函数重载纯粹是编译时构造。 它们对生成的JavaScript没有影响,因此也没有运行时成本。

函数重载的参数列表无法指定参数的默认值。 换句话说,重载只能使用?。 指定可选参数时的表格。

以下是带有重载的函数的示例。

function attr(name: string): string;  
function attr(name: string, value: string): Accessor;  
function attr(map: any): Accessor;  
function attr(nameOrMap: any, value?: string): any {  
    if (nameOrMap && typeof nameOrMap === "string") {  
        // handle string case  
    }  
    else {  
        // handle map case  
    }  
}

请注意,每个重载和最终实现均指定相同的标识符。 该声明引入的局部变量“ attr”的类型为

var attr: {  
    (name: string): string;  
    (name: string, value: string): Accessor;  
    (map: any): Accessor;  
};

请注意,实际函数实现的签名不包括在类型中。

6.3 函数实现

没有返回类型注释的函数实现被称为隐式类型的函数。隐式类型函数f的返回类型是从其函数主体中推断出来的,如下所示:

  • 如果f的函数体中没有带有表达式的return语句,则推断的返回类型为Void。
  • 否则,如果f的函数体直接引用f或引用通过同一分析引用f的任何隐式类型的函数,则推断的返回类型为Any。
  • 否则,如果f是上下文类型的函数表达式(第4.10节),则推断的返回类型是函数主体中返回语句表达式类型的并集类型(第3.4节),而忽略不包含表达式的返回语句。
  • 否则,推断的返回类型是函数主体中返回语句表达式类型的第一个,它是其他每个函数的超类型(第3.11.3节),而忽略没有表达式的返回语句。如果没有return语句表达式具有其他类型的超类型,则会发生编译时错误。

在这个例子中

function f(x: number) {  
    if (x <= 0) return x;  
    return g(x);  
}

function g(x: number) {  
    return f(x - 1);  
}

推断’f’和’g’的返回类型为Any,因为函数在没有返回类型注释的循环中引用自身。向其中一个添加显式的返回类型“数字”会中断循环,并导致为另一个推断返回类型“数字”。

一个显式类型的函数,其返回类型不是Void类型,Any类型或包含Void或Any类型作为组成部分的并集类型,必须在其主体中的某处至少具有一个return语句。此规则的例外情况是,函数实现是否包含单个“ throw”语句。

函数实现中“ this”的类型是Any类型。

在函数实现的签名中,可以通过在参数后面加上初始化程序来将其标记为可选参数。当参数声明同时包含类型注释和初始值设定项时,初始值设定项表达式将由指定的类型在上下文中键入(第4.23节),并且必须可分配给指定的类型,否则会发生编译时错误。当参数声明没有类型注释但包含初始化程序时,参数的类型是初始化程序表达式的类型的扩展形式(第3.12节)。

初始化程序表达式在函数体的范围内进行评估,但不允许引用局部变量,并且仅允许访问在其初始化的参数左侧声明的参数,除非参数引用出现在嵌套函数表达式中。

当输出目标为ECMAScript 3或5时,对于每个带有初始化程序的参数,将在生成的JavaScript中包含一条语句,该语句将默认值替换为省略的参数,如6.6节所述。这个例子

function strange(x: number, y = x * 2, z = x + y) {  
    return z;  
}

生成的Javascript等价于

function strange(x, y, z) {  
    if (y === void 0) { y = x * 2; }  
    if (z === void 0) { z = x + y; }  
    return z;  
}

在下面例子中,

var x = 1;  
function f(a = x) {  
    var x = "hello";  
}

局部变量“ x”在参数初始值设定项的范围内(因此隐藏了外部“ x”),但是引用它是错误的,因为在评估参数初始值设定项时它将始终未初始化。

6.4 析构参数声明

参数声明可以指定绑定模式(第3.9.2.2节),然后称为解构参数声明。与解构变量声明(第5.2.2节)相似,解构参数声明引入零个或多个命名的局部变量,并使用从对象或数组的参数或参数的属性或元素中提取的值对它们进行初始化。

以与通过解构变量声明引入的局部相同的方式确定在解构参数声明中引入的局部类型,除了与解构参数声明关联的类型T的确定如下:

  • 如果声明包含类型注释,则T为该类型。
  • 如果声明出现在具有上下文签名的函数表达式中(第4.10节),则T是从上下文签名获得的类型。
  • 否则,如果声明包含初始化程序表达式,则T是初始化程序表达式类型的扩展形式(第3.12节)。
  • 否则,如果声明指定了绑定模式,则T是该绑定模式的隐含类型(第5.2.3节)。
  • 否则,如果参数是rest参数,则T为any []。
  • 否则,T为任意。

当输出目标是ECMAScript 2015或更高版本时,除了删除可选的类型注释之外,在生成的JavaScript代码中,解构参数声明将保持不变。当输出目标为ECMAScript 3或5时,解构参数声明将重写为局部变量声明。

这个例子

function drawText({ text = "", location: [x, y] = [0, 0], bold = false }) {  
    // Draw text  
}

声明一个函数drawText,该函数采用单个类型的参数

{ text?: string; location?: [number, number]; bold?: boolean; }

当输出目标为ECMAScript 3或5时,该功能将重写为

function drawText(_a) {  
    var _b = _a.text,  
        text = _b === void 0 ? "" : _b,  
        _c = _a.location,  
        _d = _c === void 0 ? [0, 0] : _c,  
        x = _d[0],  
        y = _d[1],  
        _e = _a.bold,  
        bold = _e === void 0 ? false : _e;  
    // Draw text  
}

解构参数声明不允许在单个绑定模式上进行类型注释,因为此类注释将与对象文字中冒号的已经确定的含义冲突。 必须将类型注释写在顶级参数声明上。 例如

interface DrawTextInfo {  
    text?: string;  
    location?: [number, number];  
    bold?: boolean;  
}

function drawText({ text, location: [x, y], bold }: DrawTextInfo) {  
    // Draw text  
}

6.5 通用函数

函数实现可以在其签名中包含类型参数(第3.9.2.1节),然后称为泛型函数。 类型参数提供了一种机制,用于表达调用操作中参数与返回类型之间的关系。 类型参数没有运行时表示形式,它们纯粹是编译时构造。

在函数实现的签名中声明的类型参数在该函数实现的签名和主体中。

以下是通用函数的示例:

interface Comparable {  
    localeCompare(other: any): number;  
}

function compare<T extends Comparable>(x: T, y: T): number {  
    if (x == null) return y == null ? 0 : -1;  
    if (y == null) return 1;  
    return x.localeCompare(y);  
}

注意,已知“ x”和“ y”参数是约束“可比较”的子类型,因此具有“ compareTo”成员。 这将在3.6.1节中进一步描述。

可以在调用操作中显式指定对通用函数的调用的类型参数,或者在可能的情况下,可以从调用中的常规参数的类型推断出类型参数(第4.15.2节)。 在这个例子中

class Person {  
    name: string;  
    localeCompare(other: Person) {  
        return compare(this.name, other.name);  
    }  
}

因为两个参数都是字符串,所以“比较”的类型参数会自动推断为String类型。

6.6 代码生成

函数声明生成的JavaScript代码等效于:

function <FunctionName>(<FunctionParameters>) {  
    <DefaultValueAssignments>  
    <FunctionStatements>  
}

FunctionName是函数的名称(对于函数表达式,则为空)。

FunctionParameters是用逗号分隔的函数参数名称列表。

DefaultValueAssignments是默认属性值分配的序列,每个参数都有一个默认值,按照声明的顺序,具有默认值,形式为

if (<Parameter> === void 0) { <Parameter> = <Default>; }

其中Parameter是参数名称,Default是默认值表达式。

FunctionStatements是为函数主体中指定的语句生成的代码。

6.7 生成函数

TODO:文档生成函数

6.8 异步函数

TODO:文档异步函数

6.9 类型保护函数

TODO:文档类型保护函数,包括this类型预测

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值