Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Compare
  3. JavaScript vs TypeScript
  4. JavaScript to TypeScript Functions

JavaScript to TypeScript Functions

What changes when you add TypeScript types to JavaScript functions -- parameter annotations, return types, optional arguments, and overloads.

Parameter and Return Types

TypeScript requires type annotations on function parameters. This is the single biggest change from JavaScript: function greet(name) becomes function greet(name: string). Forgetting the annotation triggers a noImplicitAny error in strict mode. Return types can be annotated too (function greet(name: string): string), but they are optional -- TypeScript infers the return type from the function body. Whether to annotate return types is a team convention. Some codebases annotate everything for documentation; others rely on inference and only annotate when the inferred type is wider than intended. Arrow functions follow the same rules: (x: number) => x * 2 annotates the parameter, and the return type is inferred. When assigning an arrow function to a typed variable (const fn: (x: number) => number = (x) => x * 2), the parameter types flow from the variable type and the arrow function body does not need its own annotations. This "contextual typing" is one of TypeScript's ergonomic wins -- you define the shape once and the implementation inherits it.

Function declaration

JavaScript
function add(a, b) {
  return a + b;
}
TypeScript
function add(a: number, b: number): number {
  return a + b;
}

Arrow function with contextual typing

JavaScript
const double = (x) => x * 2;
TypeScript
// Option 1: annotate parameter
const double = (x: number) => x * 2;

// Option 2: annotate variable
const double2: (x: number) => number = (x) => x * 2;
Warm-up1 / 2

Can you write this from memory?

Define a function `greet` taking `name` as an argument.

JavaScript: JavaScript Functions Practice →

Optional and Default Parameters

JavaScript default parameters (function connect(host, port = 5432)) work identically in TypeScript. The compiler infers the type of port from the default value, so port: number is optional if the default is a number literal. TypeScript adds a separate concept: optional parameters marked with ?. Writing function greet(name: string, title?: string) means title can be omitted entirely, in which case its value is undefined. This is subtly different from a default: optional parameters have type string | undefined, while default parameters have the type of the default value. The distinction matters when designing APIs. An optional parameter signals "this might not be provided and you should handle both cases," while a default parameter signals "this always has a value even if the caller does not specify one." JavaScript conflates the two because it treats missing arguments as undefined, but TypeScript's type system distinguishes them. Rest parameters also carry types: (...nums: number[]) collects all remaining arguments into a typed array. The JavaScript ...nums syntax is unchanged; you just add : number[] after the name.

Optional parameter

JavaScript
function greet(name, title) {
  if (title) {
    return title + " " + name;
  }
  return name;
}
greet("Ada");
greet("Ada", "Dr.");
TypeScript
function greet(name: string, title?: string) {
  if (title) {
    return `${title} ${name}`;
  }
  return name;
}
greet("Ada");
greet("Ada", "Dr.");

Default parameter

JavaScript
function connect(host, port = 5432) {
  console.log(host + ":" + port);
}
TypeScript
function connect(host: string, port = 5432) {
  console.log(`${host}:${port}`);
  // port inferred as number from default
}
Warm-up1 / 2

Can you write this from memory?

Define a function `greet` taking `name` as an argument.

JavaScript: JavaScript Functions Practice →

Function Overloads

JavaScript has no function overloading -- you write one implementation and handle different argument shapes with runtime checks. TypeScript adds overload signatures: separate declarations that describe different calling conventions, followed by a single implementation that handles all cases. The overload signatures are the public API; the implementation signature is hidden from callers. This pattern shows up when a function accepts different argument types and returns different result types depending on the input. For example, a createElement function might return HTMLDivElement when called with "div" and HTMLSpanElement when called with "span." Without overloads, the return type would be the broad HTMLElement, losing specific type information at call sites. The implementation body still uses runtime checks (if typeof arg === "string"), which is identical to what you would write in JavaScript. The overload signatures just teach the compiler about the relationship between input and output types. Overloads are a power feature that most migration projects do not need immediately. They solve the specific problem of type-narrowing across argument patterns, and many codebases use union types or generic constraints instead.

Function overloads

JavaScript
function parse(input) {
  if (typeof input === "string") {
    return JSON.parse(input);
  }
  return input;
}
TypeScript
function parse(input: string): object;
function parse(input: object): object;
function parse(input: string | object): object {
  if (typeof input === "string") {
    return JSON.parse(input);
  }
  return input;
}

Typing Callbacks and Higher-Order Functions

JavaScript callbacks are just functions passed as arguments. TypeScript types them with function type syntax: (item: string) => boolean describes a function that takes a string and returns a boolean. When you pass callbacks to array methods, TypeScript already knows the element type from the array, so the callback parameters are inferred. Writing [1, 2, 3].filter(n => n > 1) infers n as number without annotation. Where explicit callback types help is in your own higher-order functions: function retry(fn: () => Promise<void>, times: number). Without the type annotation on fn, TypeScript cannot check that the function you pass matches the expected signature. The type (...args: A) => R captures both parameters and return type and can be used in generic higher-order functions to preserve the full signature through wrappers. React developers encounter this daily with event handlers (onClick: (e: React.MouseEvent) => void), component props with render callbacks, and custom hooks that accept configuration functions. The callback typing pattern translates directly from JavaScript -- you add parameter and return annotations -- but the payoff in autocomplete and error detection is immediate.

Callback type in higher-order function

JavaScript
function retry(fn, times) {
  for (let i = 0; i < times; i++) {
    try { return fn(); }
    catch (e) { if (i === times - 1) throw e; }
  }
}
TypeScript
function retry<T>(
  fn: () => T,
  times: number
): T {
  for (let i = 0; i < times; i++) {
    try { return fn(); }
    catch (e) { if (i === times - 1) throw e; }
  }
  throw new Error("unreachable");
}

Array method inference

JavaScript
const evens = [1, 2, 3, 4].filter(n => n % 2 === 0);
TypeScript
const evens = [1, 2, 3, 4].filter(n => n % 2 === 0);
// n inferred as number, evens inferred as number[]

Practice Both Languages

10 free exercises a day. No credit card required. Build syntax muscle memory with spaced repetition.

Free forever. No credit card required.

← Back to JavaScript vs TypeScript
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.