Typing parameters and return values
Functions are where types earn their keep most visibly: a function's signature is a contract — "give me these types, I return that type" — and TypeScript enforces it at every call. This lesson is typing a function's parameters and return value, and the one rule that decides most of it: annotate parameters, let the return type infer.
Parameters and return types
function greet(name: string, age: number): string {
return `${name} is ${age}`;
}
Each parameter is annotated name: type, and the return type comes after the parameter list:
: string. TypeScript now checks both ends — the call against the parameters, and the body against
the return type:
greet("Kavita", 33); // fine
greet("Kavita", "old"); // error: Argument of type 'string' is not assignable to parameter of type 'number'
greet("Kavita"); // error: Expected 2 arguments, but got 1
function bad(name: string): string {
return name.length; // error: Type 'number' is not assignable to type 'string'
}
Wrong argument types, wrong argument counts, and a body that returns the wrong type are all caught before the code runs. This is the core value: the signature is a promise, and the compiler holds both the caller and the implementation to it.
Parameters must be annotated; return types usually should not
The rule that runs through the whole course, applied to functions:
Parameters must be annotated. TypeScript cannot infer them — it has no way to know what a caller
will pass. An un-annotated parameter becomes an implicit any (a silent hole), which strict mode
correctly flags:
function greet(name) { // error under strict: Parameter 'name' implicitly has an 'any' type
return name.toUpperCase();
}
noImplicitAny (part of strict mode) makes this an error, forcing you to annotate. Always annotate
parameters.
Return types are usually inferred, and that is fine — with a caveat. TypeScript infers the return type from the body:
function add(a: number, b: number) { // return type inferred: number
return a + b;
}
You do not need to write : number — inference gets it right. So for small internal helpers, leaving
the return type to inference is clean and idiomatic. But annotate the return type of a public or
exported function, for two reasons: it documents the contract for callers, and it catches an
accidental change to what the function returns. If you annotate : string and later edit the body to
return a number, you get an error at the function — precise — rather than a confusing error at every
call site. So: infer for private helpers, annotate for public API.
Typing this, briefly
In a standalone function, this is usually not a concern in TypeScript (arrow functions and modern
code avoid it). When you do need it — an old-style callback that relies on this — TypeScript lets you
type it with a special first "parameter" that is erased at runtime:
function handleClick(this: HTMLButtonElement, event: MouseEvent) {
this.disabled = true; // `this` is typed, not a real parameter
}
You will meet this rarely (mostly with DOM APIs and legacy code); modern TypeScript with arrow
functions and classes sidesteps it. Know it exists so a this: parameter does not confuse you.
void — a function that returns nothing
A function that does not return a useful value has return type void:
function log(message: string): void {
console.log(message); // does something, returns nothing useful
}
void means "returns nothing you should use". You rarely write it — it is inferred for a function with
no return (or a bare return;) — but it appears constantly in callback types (an event handler
returns void). One subtlety worth knowing: a void return type in a callback is permissive — it
allows a function that does return something to be used where void is expected, because the caller
ignores the return value anyway:
const numbers = [1, 2, 3];
numbers.forEach(n => console.log(n)); // the arrow returns void (console.log returns undefined) — fine
This permissiveness is deliberate and occasionally surprising; for now, read void as "the return
value is ignored".
Async functions return a Promise
A function marked async always returns a Promise, and TypeScript types it precisely:
async function fetchName(): Promise<string> {
return "Kavita"; // even though you return a string, the function returns Promise<string>
}
An async function that "returns a string" actually returns a Promise<string> — the async
wraps the value in a promise. TypeScript infers this, so you can write async function fetchName()
and the return type is Promise<string> automatically. The async module goes deep on this; the point
here is that async changes the return type to a Promise, and TypeScript tracks it. Annotating
: Promise<string> on a public async function documents it clearly.
Check your work
What a function signature is. A contract — the parameter types and return type — that TypeScript enforces at every call and in the body.
What TypeScript checks. Argument types, argument count, and that the body returns the declared type.
Must parameters be annotated? Yes — TypeScript cannot infer them; an un-annotated one is an
implicit any, which noImplicitAny (strict mode) flags.
Should return types be annotated? Infer for private helpers; annotate for public/exported functions (documents the contract, catches an accidental change at the function not the call sites).
What void means. Returns nothing useful — usually inferred, common in callback types; permissive
about a callback that does return something.
What async does to the return type. Wraps it in a Promise — async () => string returns
Promise<string>.
When you meet a this: parameter. Rarely — old-style callbacks relying on this; modern code with
arrows and classes avoids it.
Practice
- Write
greet(name: string, age: number): string. Call it with wrong types, too few arguments, and correctly; read each result. - Return the wrong type from a function body and read the error at the function.
- Write a function with an un-annotated parameter under strict mode and read the implicit-
anyerror. Annotate it. - Write a small helper with no return annotation and confirm the return type is inferred. Then add a wrong explicit return type and see the error move to the function.
- Write a
voidfunction and confirm the type. Use an arrow returning a value inforEachand note it is allowed. - Write an
asyncfunction returning a string and confirm its inferred type isPromise<string>. - For three functions, decide whether to annotate the return type and justify each choice.
Official documentation
- TypeScript — Functions — Parameters, return types, and inference.
- TypeScript — noImplicitAny — Why parameters must be annotated.
- TypeScript —
thisparameters — Typingthis. - TypeScript — void — The nothing-useful return type.
Next: optional, default and rest parameters.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship