Function Types

The Course
•
5 min read

Yesterday you annotated values. Today you will annotate the boundary of a reusable operation. In function double(value: number): number, the first number describes the parameter and the second describes the return value. The function body is still JavaScript.

Parameters need annotations when the surrounding context does not supply their types. With strict checking, a bare untyped parameter produces an implicit-any diagnostic. Otherwise the checker would have no useful contract for callers. A return type can often be inferred from the body. An explicit return type is useful when you want the checker to verify your intended result as you edit the function.

A function returning void is used for an action with no useful returned value, such as printing a report. A function returning number provides data the caller can use. Logging a result is not the same as returning it.

A default parameter supplies a value when an argument is missing or undefined: count = 1. TypeScript can infer its type from that value. An optional parameter, written with ?, can be undefined and must be handled before doing arithmetic. Prefer a default when a genuine default exists. Required parameters normally come before optional parameters.

Do not annotate numeric parameters as “positive” in prose and assume the checker enforces it. The number type permits negative values; a runtime condition is still necessary if your function promises to reject them.

Predict, check, then run

Before running, write down the output and explain which information the checker knows. Click Check types first, then Check & run.

Predict, then runTypeScript
function total(minutes: number, count = 1): number {
  return minutes * count;
}

console.log(total(20));
console.log(total(20, 3));

Enable JavaScript to edit and type-check this example.

Check your prediction
20
60

When copied JavaScript needs an annotation

In JavaScript beginner you wrote functions with unannotated parameters. TypeScript does not run every possible caller to infer such a parameter’s type. With strict checking, the function below reports TS7006, an implicit-any parameter. The fix is to state the input contract, not to remove strict checking. A formatter can infer its return type from the template literal.

Find and fix the type error: untyped parameterTypeScript
function label(minutes) {
  return `${minutes} min`;
}

console.log(label(25));

Enable JavaScript to edit and type-check this example.

Check your diagnosis

Annotate the parameter as minutes: number. The function then promises to accept numbers; text conversion and validation belong before calling it. The body’s return is inferred as string.

Optional arguments and action functions

Read count?: number as “count can be omitted.” In JavaScript an omitted argument is undefined. The explicit branch below supplies an effective count before multiplication. A default parameter such as count = 1 expresses the same default more briefly. The question mark belongs to the declaration; calls still use ordinary parentheses.

printTotal has a void return type: callers use its printing action, not a returned number. In contrast, total returns a number that callers can use in another calculation. Predict all three outputs before running.

Optional arguments and voidTypeScript
function total(minutes: number, count?: number): number {
  if (count === undefined) {
    return minutes;
  }

  return minutes * count;
}

function printTotal(minutes: number): void {
  console.log(total(minutes));
}

console.log(total(20));
console.log(total(20, 0));
printTotal(30);

Enable JavaScript to edit and type-check this example.

Check your prediction
20
0
30

Read a real compiler error

The parameter is correct. Check the return value against the declared return contract.

Find and fix the type errorTypeScript
function total(minutes: number): number {
  return `${minutes} min`;
}

console.log(total(25));

Enable JavaScript to edit and type-check this example.

Check your diagnosis

The body returns a string where number was promised. If this is a calculation, return minutes. If it is a formatter, change the function name and return contract to string. Choose the intended behavior before editing.

Your turn

Exercise

Function Types: practise

Write remainingMinutes(planned, completed), with number parameters and a number return annotation. Return planned minus completed. Print 35 for (60, 25) and 0 for (20, 20).

Your workspaceTypeScript
// Define remainingMinutes, then call it with both pairs.

Enable JavaScript to edit and type-check this example.

Hint 1: the idea

The function calculates a value; it should return rather than only log it.

Hint 2: a first step

Put : number after each parameter name and after the closing parameter parenthesis.

Show one solution
One possible solutionTypeScript
function remainingMinutes(planned: number, completed: number): number {
  return planned - completed;
}

console.log(remainingMinutes(60, 25));
console.log(remainingMinutes(20, 20));

Enable JavaScript to edit and type-check this example.

35
0

Explain the types at the function boundary and the runtime checks, if any. Compare your approach before copying this answer.

Change it and explain it

Call the function with a string and read the argument diagnostic. Restore numbers, then try completed greater than planned. Decide whether the contract should allow a negative result or clamp it to zero; explain that this is a product decision, not a type-system rule.

Was this article helpful?

Your response is saved on this device.