6.3 Function Implementations

A function implementation without a return type annotation is said to be an implicitly typed function. The return type of an implicitly typed function f is inferred from its function body as follows:

  • If there are no return statements with expressions in f‘s function body, the inferred return type is Void.
  • Otherwise, if f‘s function body directly references f or references any implicitly typed functions that through this same analysis reference f, the inferred return type is Any.
  • Otherwise, if f is a contextually typed function expression (section 4.10), the inferred return type is the union type (section 3.4) of the types of the return statement expressions in the function body, ignoring return statements with no expressions.
  • Otherwise, the inferred return type is the first of the types of the return statement expressions in the function body that is a supertype (section 3.11.3) of each of the others, ignoring return statements with no expressions. A compile-time error occurs if no return statement expression has a type that is a supertype of each of the others.

In the example

  1. function f(x: number) {
  2. if (x <= 0) return x;
  3. return g(x);
  4. }
  5. function g(x: number) {
  6. return f(x - 1);
  7. }

the inferred return type for ‘f’ and ‘g’ is Any because the functions reference themselves through a cycle with no return type annotations. Adding an explicit return type ‘number’ to either breaks the cycle and causes the return type ‘number’ to be inferred for the other.

An explicitly typed function whose return type isn’t the Void type, the Any type, or a union type containing the Void or Any type as a constituent must have at least one return statement somewhere in its body. An exception to this rule is if the function implementation consists of a single ‘throw’ statement.

The type of ‘this’ in a function implementation is the Any type.

In the signature of a function implementation, a parameter can be marked optional by following it with an initializer. When a parameter declaration includes both a type annotation and an initializer, the initializer expression is contextually typed (section 4.23) by the stated type and must be assignable to the stated type, or otherwise a compile-time error occurs. When a parameter declaration has no type annotation but includes an initializer, the type of the parameter is the widened form (section 3.12) of the type of the initializer expression.

Initializer expressions are evaluated in the scope of the function body but are not permitted to reference local variables and are only permitted to access parameters that are declared to the left of the parameter they initialize, unless the parameter reference occurs in a nested function expression.

When the output target is ECMAScript 3 or 5, for each parameter with an initializer, a statement that substitutes the default value for an omitted argument is included in the generated JavaScript, as described in section 6.6. The example

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

generates JavaScript that is equivalent to

  1. function strange(x, y, z) {
  2. if (y === void 0) { y = x * 2; }
  3. if (z === void 0) { z = x + y; }
  4. return z;
  5. }

In the example

  1. var x = 1;
  2. function f(a = x) {
  3. var x = "hello";
  4. }

the local variable ‘x’ is in scope in the parameter initializer (thus hiding the outer ‘x’), but it is an error to reference it because it will always be uninitialized at the time the parameter initializer is evaluated.