5.2 Variable Statements

Variable statements are extended to include optional type annotations.

  VariableDeclaration: ( Modified )   SimpleVariableDeclaration   DestructuringVariableDeclaration

A variable declaration is either a simple variable declaration or a destructuring variable declaration.

5.2.1 Simple Variable Declarations

A simple variable declaration introduces a single named variable and optionally assigns it an initial value.

  SimpleVariableDeclaration:   BindingIdentifierTypeAnnotationoptInitializeropt

The type T of a variable introduced by a simple variable declaration is determined as follows:

  • If the declaration includes a type annotation, T is that type.
  • Otherwise, if the declaration includes an initializer expression, T is the widened form (section 3.12) of the type of the initializer expression.
  • Otherwise, T is the Any type.

When a variable declaration specifies both a type annotation and an initializer expression, the type of the initializer expression is required to be assignable to (section 3.11.4) the type given in the type annotation.

Multiple declarations for the same variable name in the same declaration space are permitted, provided that each declaration associates the same type with the variable.

When a variable declaration has a type annotation, it is an error for that type annotation to use the typeof operator to reference the variable being declared.

Below are some examples of simple variable declarations and their associated types.

  1. var a; // any
  2. var b: number; // number
  3. var c = 1; // number
  4. var d = { x: 1, y: "hello" }; // { x: number; y: string; }
  5. var e: any = "test"; // any

The following is permitted because all declarations of the single variable ‘x’ associate the same type (Number) with ‘x’.

  1. var x = 1;
  2. var x: number;
  3. if (x == 1) {
  4. var x = 2;
  5. }

In the following example, all five variables are of the same type, ‘{ x: number; y: number; }’.

  1. interface Point { x: number; y: number; }
  2. var a = { x: 0, y: <number> undefined };
  3. var b: Point = { x: 0, y: undefined };
  4. var c = <Point> { x: 0, y: undefined };
  5. var d: { x: number; y: number; } = { x: 0, y: undefined };
  6. var e = <{ x: number; y: number; }> { x: 0, y: undefined };

5.2.2 Destructuring Variable Declarations

A destructuring variable declaration introduces zero or more named variables and initializes them with values extracted from properties of an object or elements of an array.

  DestructuringVariableDeclaration:   BindingPatternTypeAnnotationoptInitializer

Each binding property or element that specifies an identifier introduces a variable by that name. The type of the variable is the widened form (section 3.12) of the type associated with the binding property or element, as defined in the following.

TODO: Document destructuring an iterator into an array.

The type T associated with a destructuring variable declaration is determined as follows:

  • If the declaration includes a type annotation, T is that type.
  • Otherwise, if the declaration includes an initializer expression, T is the type of that initializer expression.
  • Otherwise, T is the Any type.

The type T associated with a binding property is determined as follows:

  • Let S be the type associated with the immediately containing destructuring variable declaration, binding property, or binding element.
  • If S is the Any type:
    • If the binding property specifies an initializer expression, T is the type of that initializer expression.
    • Otherwise, T is the Any type.
  • Let P be the property name specified in the binding property.
  • If S has an apparent property with the name P, T is the type of that property.
  • Otherwise, if S has a numeric index signature and P is a numerical name, T is the type of the numeric index signature.
  • Otherwise, if S has a string index signature, T is the type of the string index signature.
  • Otherwise, no type is associated with the binding property and an error occurs.

The type T associated with a binding element is determined as follows:

  • Let S be the type associated with the immediately containing destructuring variable declaration, binding property, or binding element.
  • If S is the Any type:
    • If the binding element specifies an initializer expression, T is the type of that initializer expression.
    • Otherwise, T is the Any type.
  • If S is not an array-like type (section 3.3.2), no type is associated with the binding property and an error occurs.
  • If the binding element is a rest element, T is an array type with an element type E, where E is the type of the numeric index signature of S.
  • Otherwise, if S is a tuple-like type (section 3.3.3):
    • Let N be the zero-based index of the binding element in the array binding pattern.
    • If S has a property with the numerical name N, T is the type of that property.
    • Otherwise, no type is associated with the binding element and an error occurs.
  • Otherwise, if S has a numeric index signature, T is the type of the numeric index signature.
  • Otherwise, no type is associated with the binding element and an error occurs.

When a destructuring variable declaration, binding property, or binding element specifies an initializer expression, the type of the initializer expression is required to be assignable to the widened form of the type associated with the destructuring variable declaration, binding property, or binding element.

TODO: Update rules to reflect improved checking of destructuring with literal initializers.

When the output target is ECMAScript 2015 or higher, except for removing the optional type annotation, destructuring variable declarations remain unchanged in the emitted JavaScript code.

When the output target is ECMAScript 3 or 5, destructuring variable declarations are rewritten to simple variable declarations. For example, an object destructuring declaration of the form

  1. var { x, p: y, q: z = false } = getSomeObject();

is rewritten to the simple variable declarations

  1. var _a = getSomeObject(),
  2. x = _a.x,
  3. y = _a.p,
  4. _b = _a.q,
  5. z = _b === void 0 ? false : _b;

The ‘_a’ and ‘_b’ temporary variables exist to ensure the assigned expression is evaluated only once, and the expression ‘void 0’ simply denotes the JavaScript value ‘undefined’.

Similarly, an array destructuring declaration of the form

  1. var [x, y, z = 10] = getSomeArray();

is rewritten to the simple variable declarations

  1. var _a = getSomeArray(),
  2. x = _a[0],
  3. y = _a[1],
  4. _b = _a[2],
  5. z = _b === void 0 ? 10 : _b;

Combining both forms of destructuring, the example

  1. var { x, p: [y, z = 10] = getSomeArray() } = getSomeObject();

is rewritten to

  1. var _a = getSomeObject(),
  2. x = _a.x,
  3. _b = _a.p,
  4. _c = _b === void 0 ? getSomeArray() : _b,
  5. y = _c[0],
  6. _d = _c[1],
  7. z = _d === void 0 ? 10 : _d;

5.2.3 Implied Type

A variable, parameter, binding property, or binding element declaration that specifies a binding pattern has an implied type which is determined as follows:

  • If the declaration specifies an object binding pattern, the implied type is an object type with a set of properties corresponding to the specified binding property declarations. The type of each property is the type implied by its binding property declaration, and a property is optional when its binding property declaration specifies an initializer expression.
  • If the declaration specifies an array binding pattern without a rest element, the implied type is a tuple type with elements corresponding to the specified binding element declarations. The type of each element is the type implied by its binding element declaration.
  • If the declaration specifies an array binding pattern with a rest element, the implied type is an array type with an element type of Any.

The implied type of a binding property or binding element declaration is

  • the type of the declaration’s initializer expression, if any, or otherwise
  • the implied type of the binding pattern specified in the declaration, if any, or otherwise
  • the type Any.

In the example

  1. function f({ a, b = "hello", c = 1 }) { ... }

the implied type of the binding pattern in the function’s parameter is ‘{ a: any; b?: string; c?: number; }’. Since the parameter has no type annotation, this becomes the type of the parameter.

In the example

  1. var [a, b, c] = [1, "hello", true];

the array literal initializer expression is contextually typed by the implied type of the binding pattern, specifically the tuple type ‘[any, any, any]’. Because the contextual type is a tuple type, the resulting type of the array literal is the tuple type ‘[number, string, boolean]’, and the destructuring declaration thus gives the types number, string, and boolean to a, b, and c respectively.