Parameters and return types
Functions are where types earn their keep. Annotate each parameter, then the return type after the parameter list:
function priceWithTax(price: number, taxRate: number): number { return price + price * taxRate; }
Read it aloud: priceWithTax takes two numbers and returns a number. Every mistake now becomes a compile error:
| Mistake | What the compiler says |
|---|---|
priceWithTax("100", 0.2) | A string is not a number |
priceWithTax(100) | An argument is missing |
return "expensive" in the body | That is not a number |
In the JavaScript course all of this lived in a programmer's memory. Here the compiler holds it instead, and it never gets tired or distracted.
A typed function, checked at every call
function priceWithTax(price: number, taxRate: number): number { return price + price * taxRate; } console.log(priceWithTax(100, 0.2)); console.log(priceWithTax(50, 0.1));
Output
120 55
Both calls pass two numbers, so both type-check and the arithmetic runs as expected.
A call like priceWithTax("100", 0.2) is rejected instead, with an error saying that an argument of type 'string' is not assignable to a parameter of type 'number'. Notice how specific that message is: it names the offending type, the expected type, and the position. In plain JavaScript the same call would have run, concatenated "100" with the product, and produced the nonsense string "10020" far away from the mistake.
A function that returns a boolean
Return annotations work the same way, and they document the answer's shape as clearly as parameters document the inputs.
function canVote(age: number): boolean { return age >= 18; } const first = canVote(20); console.log("age 20 can vote: " + first); console.log("age 12 can vote: " + canVote(12));
Output
age 20 can vote: true age 12 can vote: false
What the annotation is doing
age >= 18evaluates totrueorfalse, which matches the declaredbooleanreturn type, so the body keeps its promise.- Declaring the return type as
stringinstead would put the error inside the function, on thereturnline, rather than at the call sites. That is a useful signal: it means the body contradicted its own signature, so the function is at fault rather than its callers. - Because the return type is inferred correctly here anyway, the annotation is documentation. Its value shows up later, when a longer function grows a second
returnthat accidentally hands back something else.
Which call satisfies a one-number signature
Given function double(n: number): number, the call that compiles is double(4).
The signature demands exactly one argument of type number, and that is what the call supplies. Three kinds of call fail instead:
double("4")fails because a string is not a number, even though the string looks numeric.double()fails because the argument is missing.double(4, 8)fails because there is an extra argument.
That last pair is a real departure from JavaScript, where extra arguments are silently ignored and missing ones silently become undefined. TypeScript checks arity as well as types.
What you must annotate, and what you may skip
Return types are usually inferred: if the body returns price + price * taxRate, TypeScript already knows the function returns a number. Many teams still write return types on exported functions as documentation.
Parameters are different. TypeScript cannot guess what callers will pass, so an unannotated parameter silently becomes type any, meaning unchecked. In strict mode (which every serious project turns on, and which you will meet in the final unit) that is the error Parameter 'x' implicitly has an 'any' type.
Rule of thumb: always annotate parameters, let everything else be inferred until you have a reason not to.
describePet: two parameters, one returned sentence
describePet(name: string, age: number): string builds a sentence like "Milo is 3 years old" and hands it back to the caller.
function describePet(name: string, age: number): string { return name + " is " + age + " years old"; } console.log(describePet("Milo", 3)); console.log(describePet("Luna", 7));
Output
Milo is 3 years old Luna is 7 years old
Reading the signature
- Two annotated parameters sit inside the parentheses, and the return type
: stringcomes after the closing parenthesis. That position is fixed. - The function returns the string rather than printing it. Printing is left to the
console.logcalls, which keepsdescribePetreusable: a caller could put the sentence in a web page, a log line, or a test assertion instead. - Both parameters are used in the returned expression, and swapping them, as in
describePet(3, "Milo"), is caught at compile time because the types no longer line up positionally.