本文档的其余部分是TypeScript编程语言的正式规范,旨在作为ECMAScript 2015 Language Specification(特别是ECMA-262标准,第6版)的附件来阅读。 本文档描述了TypeScript所添加的语法,以及TypeScript编译器执行的编译时处理和类型检查,但是由于ECMAScript规范涵盖了该内容,因此它仅极少讨论了程序的运行时行为。
在整个文档中,使用ECMAScript语法的现有约定和生产名称来指定TypeScript语言添加的语法。 在TypeScript增强现有语法产生的地方,就这样指出。 例如:
声明:(已修改)
…
接口声明
类型别名声明
枚举声明
“(Modified”批注指示已替换现有语法产生式,并且“…”引用原始语法产生式的内容。
与ECMAScript语法相似,如果在语法的产生式的右侧出现短语“ [此处没有LineTerminator]”,则表示如果LineTerminator出现在语法中的输入流中,则表示产生的结果不匹配。 指示位置。
TypeScript编译器的核心目的是跟踪程序中的命名实体,并根据其指定的含义验证它们的使用。 TypeScript中的名称可以根据上下文以多种方式编写。 具体来说,名称可以写为
最常见的是,名称的编写要符合标识符的产生,即不是保留字的任何IdentifierName。
以下关键字是保留关键字,不能用作标识符:
break case catch class
const continue debugger default
delete do else enum
export extends false finally
for function if import
in instanceof new null
return super switch this
throw true try typeof
var void while with
以下关键字不能在严格模式代码中用作标识符,但在其它方面不受限制:
implements interface let package
private protected public static
yield
以下关键字不能用作用户定义的类型名称,但不受其他限制:
any boolean number string
symbol
以下关键字在某些情况下具有特殊含义,但是有效的标识符:
abstract as async await
constructor declare from get
is module namespace of
require set type
以下是ECMAScript语法的PropertyName产生:
PropertyName:
LiteralPropertyName
ComputedPropertyName
LiteralPropertyName:
标识符名称
字符串字面量
数值文学
ComputedPropertyName:
[AssignmentExpression]
属性名称可以是任何标识符(包括保留字),字符串文字,数字文字或计算出的属性名称。 字符串文字可用于为属性名称提供无效标识符,例如包含空格的名称。 数字文字属性名称等效于字符串文字属性名称,其中包含数字文字的字符串表示形式,如ECMAScript规范中所定义。
ECMAScript 2015允许对象文字和类使用计算的属性名称声明成员。 计算的属性名称指定一个表达式,该表达式在运行时计算实际的属性名称。 由于最终属性名称在编译时未知,因此TypeScript只能对使用计算的属性名称声明的实体执行有限的检查。 但是,可以在需要PropertyName的任何地方使用计算出的属性名称的子集(称为众所周知的符号),包括类型内的属性名称。 如果计算的属性名称具有以下形式,则它是一个众所周知的符号:
[ Symbol . xxx ]
在众所周知的符号中,点右边的标识符必须表示全局变量’Symbol’类型的原始类型符号的属性,否则会发生错误。
在指定ComputedPropertyName的PropertyName中,除非属性名称出现在对象文字(第4.5节)的属性赋值或非环境类(第8.4节)的属性成员声明中,否则需要计算出的属性名称来表示众所周知的符号 )。
下面是一个接口的示例,该接口使用众所周知的符号名称声明属性:
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
TODO:更新以处理computed property names with literal expressions。
声明在其关联的声明空间中引入名称。 名称在其声明空间中必须是唯一的,并且可以表示值,类型或命名空间或它们的某种组合。 实际上,一个名称最多可以具有三个不同的含义。 例如:
var X: string; // Value named X
type X = number; // Type named X
namespace X { // Namespace named X
type Y = string;
}
表示值的名称具有关联的类型(第3节),可以在表达式中引用(第4.3节)。 表示类型的名称可以单独在类型引用中使用,也可以在类型引用中的点的右侧使用(第3.8.2节)。 表示命名空间的名称可以在类型引用中点的左侧使用。
当引用具有多种含义的名称时,引用所处的上下文将确定含义。 例如:
var n: X; // X references type
var s: X.Y = X; // First X references namespace, second X references value
在第一行中,X引用类型X,因为它出现在类型位置。在第二行中,第一个X引用命名空间X,因为它出现在类型名称中的点之前,而第二个X引用变量X,因为它出现在表达式中。
声明为其声明的名称引入以下含义:
以下是一些声明示例,它们为名称引入了多种含义:
class C { // Value and type named C
x: string;
}
namespace N { // Value and namespace named N
export var x: string;
}
声明空间的存在如下:
没有顶级导入或导出声明的源文件中的顶级声明属于全局命名空间。具有一个或多个顶级导入或导出声明的源文件中的顶级声明属于该源文件表示的模块。
实体的容器定义如下:
实体的根容器定义如下:
直观地,实体的根容器是可从中访问实体的最外面的模块或命名空间主体。
接口,枚举和命名空间是“开放式”的,这意味着相对于公共根具有相同限定名称的接口,枚举和命名空间声明会自动合并。 有关更多详细信息,请参见第7.2节、第9.3节和第10.5节。
类中的实例成员和静态成员位于单独的声明空间中。 因此,以下内容是允许的:
class C {
x: number; // Instance member
static x: string; // Static member
}
名称的范围是程序文本的区域,在该区域内可以引用该名称声明的实体而无需对该名称进行限定。 名称的范围取决于声明名称的上下文。 下面按从最外到最内的顺序列出了上下文:
作用域可能会重叠,例如通过嵌套命名空间和函数。当两个名称的范围重叠时,具有最内部声明的名称将具有优先权,并且不能或只能通过限定来访问外部名称。
当标识符解析为PrimaryExpression(第4.3节)时,仅考虑作用域中具有值含义的名称,而忽略其他名称。
当标识符解析为TypeName时(第3.8.2节),仅考虑作用域中具有类型含义的名称,而忽略其他名称。
当标识符解析为NamespaceName(第3.8.2节)时,仅考虑作用域中具有命名空间含义的名称,而忽略其他名称。
待办事项:包括用于别名解析的特定规则。
请注意,类和接口成员永远不会直接在作用域中,只能通过将点(’.’)运算符应用于类或接口实例来访问它们。这甚至包括构造函数或成员函数中当前实例的成员,可通过将点运算符应用于此成员来访问它们。
正如上述规则所暗示的那样,在命名空间中本地声明的实体在范围上比在相同命名空间的其他命名空间声明中声明的导出实体更近。例如:
var x = 1;
namespace M {
export var x = 2;
console.log(x); // 2
}
namespace M {
console.log(x); // 2
}
namespace M {
var x = 3;
console.log(x); // 3
}