Always Annotate — TS Can't Guess Callers
Parameter Types
Function parameters are one of the few places annotations are required. TS can't infer them from inside the function.
What you'll learn
- Annotate every parameter
- Use object parameters
- Pick destructured vs object-style
Parameters need annotations. TS can’t see what callers will pass, so it doesn’t infer them.
Basic
function send(email: string, body: string) {
// ...
} Each parameter gets : Type.
Object Parameters
For functions with many parameters, take an object:
function send(args: { to: string; subject: string; body: string }) {
// args.to, args.subject, args.body
}
send({ to: "ada@x.com", subject: "Hi", body: "Hello" }); Reads better at the call site than send("ada@x.com", "Hi", "Hello")
— the caller sees what each value means.
Destructured Parameters
function send({ to, subject, body }: { to: string; subject: string; body: string }) {
console.log(to, subject, body);
} The pattern { to, subject, body } destructures; the : annotates
the whole object’s shape. Same as inline.
With a Named Type
type Email = {
to: string;
subject: string;
body: string;
};
function send({ to, subject, body }: Email) {
// ...
} Cleaner when the type is reused.
Default Parameters
function greet(name: string, greeting = "Hi"): string {
return `${greeting}, ${name}`;
} The default value lets TS infer the parameter’s type. Here
greeting is inferred as string.
Optional Parameters
function send(to: string, cc?: string) {
if (cc) {
// cc is string in this branch
}
} ?: says the caller can omit it. Inside the function, cc is
string | undefined — narrow before using.
Rest Parameters
function tag(name: string, ...args: string[]): string {
return `<${name}>${args.join("")}</${name}>`;
}
tag("p", "Hello", " ", "world"); ...args: string[] collects remaining arguments as a typed array.
Up Next
Return types — when to annotate, when to infer.
Return Types →