2.3 Declarations

Declarations introduce names in their associated declaration spaces. A name must be unique in its declaration space and can denote a value, a type, or a namespace, or some combination thereof. Effectively, a single name can have as many as three distinct meanings. For example:

  1. var X: string; // Value named X
  2. type X = number; // Type named X
  3. namespace X { // Namespace named X
  4. type Y = string;
  5. }

A name that denotes a value has an associated type (section 3) and can be referenced in expressions (section 4.3). A name that denotes a type can be used by itself in a type reference or on the right hand side of a dot in a type reference (3.8.2). A name that denotes a namespace can be used one the left hand side of a dot in a type reference.

When a name with multiple meanings is referenced, the context in which the reference occurs determines the meaning. For example:

  1. var n: X; // X references type
  2. var s: X.Y = X; // First X references namespace, second X references value

In the first line, X references the type X because it occurs in a type position. In the second line, the first X references the namespace X because it occurs before a dot in a type name, and the second X references the variable X because it occurs in an expression.

Declarations introduce the following meanings for the name they declare:

  • A variable, parameter, function, generator, member variable, member function, member accessor, or enum member declaration introduces a value meaning.
  • An interface, type alias, or type parameter declaration introduces a type meaning.
  • A class declaration introduces a value meaning (the constructor function) and a type meaning (the class type).
  • An enum declaration introduces a value meaning (the enum instance) and a type meaning (the enum type).
  • A namespace declaration introduces a namespace meaning (the type and namespace container) and, if the namespace is instantiated (section 10.1), a value meaning (the namespace instance).
  • An import or export declaration introduces the meaning(s) of the imported or exported entity.

Below are some examples of declarations that introduce multiple meanings for a name:

  1. class C { // Value and type named C
  2. x: string;
  3. }
  4. namespace N { // Value and namespace named N
  5. export var x: string;
  6. }

Declaration spaces exist as follows:

  • The global namespace, each module, and each declared namespace has a declaration space for its contained entities (whether local or exported).
  • Each module has a declaration space for its exported entities. All export declarations in the module contribute to this declaration space.
  • Each declared namespace has a declaration space for its exported entities. All export declarations in the namespace contribute to this declaration space. A declared namespace’s declaration space is shared with other declared namespaces that have the same root container and the same qualified name starting from that root container.
  • Each class declaration has a declaration space for instance members and type parameters, and a declaration space for static members.
  • Each interface declaration has a declaration space for members and type parameters. An interface’s declaration space is shared with other interfaces that have the same root container and the same qualified name starting from that root container.
  • Each enum declaration has a declaration space for its enum members. An enum’s declaration space is shared with other enums that have the same root container and the same qualified name starting from that root container.
  • Each type alias declaration has a declaration space for its type parameters.
  • Each function-like declaration (including function declarations, constructor declarations, member function declarations, member accessor declarations, function expressions, and arrow functions) has a declaration space for locals and type parameters. This declaration space includes parameter declarations, all local var and function declarations, and local let, const, class, interface, type alias, and enum declarations that occur immediately within the function body and are not further nested in blocks.
  • Each statement block has a declaration space for local let, const, class, interface, type alias, and enum declarations that occur immediately within that block.
  • Each object literal has a declaration space for its properties.
  • Each object type literal has a declaration space for its members.

Top-level declarations in a source file with no top-level import or export declarations belong to the global namespace. Top-level declarations in a source file with one or more top-level import or export declarations belong to the module represented by that source file.

The container of an entity is defined as follows:

  • The container of an entity declared in a namespace declaration is that namespace declaration.
  • The container of an entity declared in a module is that module.
  • The container of an entity declared in the global namespace is the global namespace.
  • The container of a module is the global namespace.

The root container of an entity is defined as follows:

  • The root container of a non-exported entity is the entity’s container.
  • The root container of an exported entity is the root container of the entity’s container.

Intuitively, the root container of an entity is the outermost module or namespace body from within which the entity is reachable.

Interfaces, enums, and namespaces are “open ended,” meaning that interface, enum, and namespace declarations with the same qualified name relative to a common root are automatically merged. For further details, see sections 7.2, 9.3, and 10.5.

Instance and static members in a class are in separate declaration spaces. Thus the following is permitted:

  1. class C {
  2. x: number; // Instance member
  3. static x: string; // Static member
  4. }