语句(Statements)
这一节讲述ts为js语句提供的静态类型检测。ts本身并没有引入任何新的语句结构,但是它扩展了本地声明的语法,如接口、类型别名和枚举声明。
5.1 块(blocks)
块扩展了包括本地接口、类型别名和枚举声明(ECMAScript 2015 语法中已经存在类)在内的语法。
Declaration: ( Modified )
…
InterfaceDeclaration
TypeAliasDeclaration
EnumDeclaration
声明:
...
接口声明
类型别名声明
枚举声明
本地类、接口、类型别名和枚举声明都是块作用域,与”let”和“const”声明相似。
什么是变量语句?
变量语句包括可选的类型注释
VariableDeclaration: ( Modified )
SimpleVariableDeclaration
DestructuringVariableDeclaration
变量声明:
简单变量声明
析构变量声明
变量语句是一个简单变量声明或者一个析构变量声明。
5.1.1 简单变量声明(Simple Variable Declaration)
简单变量声明引入了一个命名变量和可选的初始化值。
SimpleVariableDeclaration:
BindingIdentifier TypeAnnotationopt Initializeropt
简单变量声明:
绑定标识符 类型注释(可选) 初始化(可选)
简单变量声明引入了一个类型“T”,“T”由以下条件决定:
- 如果声明包含类型注释,那么“T”就是此类型。
- 否则,如果声明包含一个初始化表达式,那么“T”就是初始化表达式类型的宽泛格式(widened form)。
- 否则,“T”是“Any”类型。
当一个变量声明同时指定了一个类型注释和一个初始化表达式,初始化表达式的类型必须能够赋给给定的类型注释。
同一声明空间中的相同变量名允许具有多个声明,允许每个声明与相同类型相关联。
当一个变量声明具有类型注释时,此类型注释不能用“typeof”操作符来引用已声明的变量。
下面是一些简单变量声明和它们的相关类型。
var a; // any var b: number; // number var c = 1; // number var d = { x: 1, y: "hello" }; // { x: number; y: string; } var e: any = "test"; // any
下面的声明是允许的,因为变量名为“x”的所有声明的相关类型都是“Number”类型。
var x = 1; var x: number; if (x == 1) { var x = 2; }
以下几个变量的类型都是相同的“{ x: number; y: number; }”。
interface Point { x: number; y: number; } var a = { x: 0, y: <number> undefined }; var b: Point = { x: 0, y: undefined }; var c = <Point> { x: 0, y: undefined }; var d: { x: number; y: number; } = { x: 0, y: undefined }; var e = <{ x: number; y: number; }> { x: 0, y: undefined };
5.1.2 析构变量声明(Destructuring Variable Declaration)
析构变量声明引入零个或多个命名变量并对其初始化,这些初始化值来源于对象的属性或数组元素。
DestructuringVariableDeclaration:
BindingPattern TypeAnnotationopt Initializer
析构变量声明:
绑定模式 类型注释(可选) 初始化
每一个指定了标识符的绑定特性或者绑定元素都引入了一个变量。变量的类型是与绑定特性或者绑定元素相关类型的宽泛格式。
1、析构变量声明的相关类型”T”由以下条件决定:
- 如果声明包含类型注释,那么“T”就是此类型。
- 否则,如果声明包含一个初始化表达式,那么“T”就是初始化表达式的类型。
- 否则,“T”是“Any”类型。
2、绑定特性的相关类型“T”由以下条件决定:
- 使直接包含析构变量声明、绑定特性或绑定元素的相关类型为“S”。
- 如果“S”是“Any”类型:
- 如果绑定特性指定了一个初始化表达式,那么“T”就是初始化表达式的类型。
- 否则,“T”是“Any”类型。
- 使“P”为绑定特性的名称。
- 如果“S”具有名为“P”的显性特性,那么“T”就是此特性的类型。
- 否则,如果“S”具有一个数字索引签名并且“P”是一个数字名称,那么“T”就是数字索引签名的类型。
- 否则,如果“S”具有一个字符串索引签名,那么“T”就是字符串索引签名的类型。
- 否则,绑定特性不与任何类型相关联,此时报错。
3、绑定元素的相关类型“T”由以下条件决定:
- 使直接包含析构变量声明、绑定特性或绑定元素的相关类型为“S”。
- 如果“S”是“Any”类型:
- 如果绑定元素指定了一个初始化表达式,那么“T”就是初始化表达式的类型。
- 否则,“T”是“Any”类型。
- 如果“S”不是一个类数组类型,那么绑定特性不与任何类型相关联,此时报错。
- 如果绑定元素是一个可变元素,“T"是一个具有元素类型”E“的数组类型,”E"是数字索引签名”S"的类型。
- 否则,如果“S”是一个类元组类型:
- 数组绑定模式中绑定元素的索引是基于零的,使此索引为”N"。
- 如果“S”具有一个数字名为“N"的特性,那么”T"就是特性的类型。
- 否则,绑定元素不与任何类型相关联,此时报错。
- 否则,如果“S”具有一个数字索引签名,那么“T”就是数字索引签名的类型。
- 否则,绑定特性不与任何类型相关联,此时报错。
当一个析构变量声明、绑定特性或变量元素指定了一个初始化表达式时,此初始化表达式的类型必须能够赋给相关类型的宽泛形式。
当输出目标是 ECMAScript 2015或更高时,除非移除可选类型注释,析构变量声明在生成的js代码中保持不变。
当输出目标是 ECMAScript 3或5时,析构变量声明被重写为简单变量声明。例如,以下形式的对象析构声明:
var { x, p: y, q: z = false } = getSomeObject();
被重写为简单变量声明,如下所示:
var _a = getSomeObject(), x = _a.x, y = _a.p, _b = _a.q, z = _b === void 0 ? false : _b;
变量“_a”和“_b”确保赋值表达式只计算一次,“void 0"表达式指代了js值”undefined"。
同样地、一个如下格式的数组析构声明
var [x, y, z = 10] = getSomeArray();
被重写为简单变量声明,如下所示:
var _a = getSomeArray(), x = _a[0], y = _a[1], _b = _a[2], z = _b === void 0 ? 10 : _b;
我们将两种格式的析构声明组合起来
var { x, p: [y, z = 10] = getSomeArray() } = getSomeObject();
它被重写为:
var _a = getSomeObject(), x = _a.x, _b = _a.p, _c = _b === void 0 ? getSomeArray() : _b, y = _c[0], _d = _c[1], z = _d === void 0 ? 10 : _d;
5.1.3 隐式类型(Implied Type)
一个变量,参数,绑定特性或绑定元素声明指定了一个具有隐式类型的绑定模式,这种隐式类型由以下条件决定:
- 如果声明指定了一个对象绑定模式,那么隐式类型就是一个对象类型,这个对象类型包含了相对于声明的特性集合。当它的绑定特性声明指定了一个初始化表达式时,特性是可选的。
- 如果声明指定了一个无可变元素的数组绑定模式,那么此隐含类型就是具有相关元素的元组类型。
- 如果声明指定了一个可变元素的数组绑定模式,那么此隐含类型就是一个数组类型,此数组包含"Any“类型的元素。
绑定特性或者绑定元素声明的隐含类型是:
- 声明的初始化表达式的类型,否则
- 声明中指定的绑定模式的隐含类型,否则
- ”Any“类型。
下例中:
function f({ a, b = "hello", c = 1 }) { ... }
函数参数的绑定模式的隐含类型是”{ a: any; b?: string; c?: number; }“。
下例中:
var [a, b, c] = [1, "hello", true];
数组的初始化表达式的类型是绑定模式的隐含类型上下文决定的。因为上下文类型是一个元组类型“[any, any, any]”,结果类型是元组类型“[number, string, boolean]",所以析构声明为”a","b"和“c"相应地赋予了"number","string"和“boolean"类型。
5.2 常量声明(Let and Const Declarations)
Let和Const声明包含可选的类型注释。
LexicalBinding: ( Modified )
SimpleLexicalBinding
DestructuringLexicalBinding
SimpleLexicalBinding:
BindingIdentifier TypeAnnotationopt Initializeropt
DestructuringLexicalBinding:
BindingPattern TypeAnnotationopt Initializeropt
词法绑定:
简单词法绑定
析构词法绑定
简单词法绑定:
绑定标识符 类型注释(可选) 初始化(可选)
析构词法绑定:
绑定模式 类型注释(可选) 初始化(可选)
5.3 If,Do和While语句
if,do和while表达式可以是任何类型,而不仅仅是布尔类型。
5.4 For语句
for语句中的变量声明与变量语句中的变量声明类似。
5.5 For-In语句
在下面格式的for-in语句当中:
for (v in expr) statement
“v”必须是作为“Any”或“String”原始类型引用的表达式。“expr”必须是“Any”类型,对象类型(Object)或者类型参数(type parameter)类型。
在下面格式的“for-in”语句当中:
for (var v in expr) statement
“v”必须是一个无类型注释的变量声明,它声明了一个“Any”类型的变量。“expr”必须是“Any”,对象类型(Object)或者类型参数(type parameter)类型。
5.6 For-Of语句
5.7 Continue语句
continue语句必须直接或间接地嵌套在循环(do、while、for或for-in)语句中。当continue语句包含一个目标标签时,此标签必须出现在一个封闭的(但不能越过函数界限)循环语句的标签组中。
5.8 Break语句
break语句必须直接或间接地嵌套在循环(do、while、for或for-in)或者switch语句中。当break语句包含一个目标标签时,此标签必须出现在一个封闭(但不能越过函数界限)语句的标签组中。
5.9 Return语句
return语句必须包含在函数体内,即return语句不允许存在于命名空间体或全局层中。
无表达式的return语句返回值是“underfined”,它能存在于任何函数体中。
当一个表达式包含return语句,如果函数具有返回类型注释,那么返回表达式类型由返回类型上下文决定,此类型必须能够赋给返回类型。否则,如果函数由一个类型“T”上下文决定,那么表达式就由“T”的返回类型上下文决定。
在一个没有返回类型注释的函数实现中,返回类型由return语句推断而得。
下例中:
function f(): (x: string) => number { return s => s.length; }
return语句中的lambda 表达式的类型是有”f“函数的返回类型上下文决定的。
5.10 With语句
5.11 Swith语句
switch语句中的每一个case表达式类型必须与switch表达式类型相匹配。
5.12 Throw语句
throw语句中的表达式可以是任何类型的。
5.13 Try语句
try语句的catch子句引入的变量通常是任何类型的。catch子句不能包含类型注释。