Closures
Arrow functions and function expressions capture variables by reference, exactly as in JavaScript. A local that a closure captures and something reassigns lives in a heap cell shared by the frame and every closure; variables that are never reassigned are captured by value, which costs nothing extra. for (let ...) and for...of give each iteration its own binding.
ts
// Closures capture variables by reference: a counter factory, a callback that reassigns a
// captured local, and per-iteration `let` bindings exactly as in JavaScript.
function counter(start: number): () => number {
let n = start;
return (): number => {
n += 1;
return n;
};
}
const next = counter(10);
next();
console.log(next(), next());
let total = 0;
[1, 2, 3].forEach((x: number): void => {
total += x;
});
console.log("total", total);
const printers: (() => string)[] = [];
for (let i = 0; i < 3; i++) {
printers.push((): string => `i=${i}`);
}
console.log(printers.map((p: () => string): string => p()).join(" "));
function compose(f: (x: number) => number, g: (x: number) => number): (x: number) => number {
return (x: number): number => g(f(x));
}
const incThenDouble = compose(
(x: number): number => x + 1,
(x: number): number => x * 2,
);
console.log(incThenDouble(5));text
12 13
total 6
i=0 i=1 i=2
12Functions are values: they can be stored in arrays and fields, passed, returned and compared with ===.
Limits today
thisinside afunctionexpression is rejected; use an arrow function or a class method.- A
functiondeclaration nested inside another function or block is rejected (CS1000); bind an arrow function withconst, or move it to the top level. - Built-ins such as
StringorMath.floorcannot be passed as values (CS1246); wrap them:xs.map((x) => String(x)). - Reading a narrowed variable that a closure reassigns, after a call that might run the closure, is rejected (CS1241), because tsc's narrowing can be stale there.
Next: Map and Set.