ℹ️ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | PASS | download_stamp > now() - 6 MONTH | 0 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | PASS | meta_canonical IS NULL OR = '' OR = src_unparsed | Not set |
| Property | Value |
|---|---|
| URL | https://www.typescriptlang.org/docs/handbook/2/functions.html |
| Last Crawled | 2026-04-09 08:31:42 (5 hours ago) |
| First Indexed | 2020-08-31 20:59:00 (5 years ago) |
| HTTP Status Code | 200 |
| Meta Title | TypeScript: Documentation - More on Functions |
| Meta Description | Learn about how Functions work in TypeScript. |
| Meta Canonical | null |
| Boilerpipe Text | Functions are the basic building block of any application, whether they’re local functions, imported from another module, or methods on a class.
They’re also values, and just like other values, TypeScript has many ways to describe how functions can be called.
Let’s learn about how to write types that describe functions.
Function Type Expressions
The simplest way to describe a function is with a
function type expression
.
These types are syntactically similar to arrow functions:
ts
function
greeter
(
fn
: (
a
:
string
)
=>
void
) {
fn
(
"Hello, World"
);
}
function
printToConsole
(
s
:
string
) {
console
.
log
(
s
);
}
greeter
(
printToConsole
);
Try
The syntax
(a: string) => void
means “a function with one parameter, named
a
, of type
string
, that doesn’t have a return value”.
Just like with function declarations, if a parameter type isn’t specified, it’s implicitly
any
.
Note that the parameter name is
required
. The function type
(string) => void
means “a function with a parameter named
string
of type
any
“!
Of course, we can use a type alias to name a function type:
ts
type
GreetFunction
= (
a
:
string
)
=>
void
;
function
greeter
(
fn
:
GreetFunction
) {
// ...
}
Try
Call Signatures
In JavaScript, functions can have properties in addition to being callable.
However, the function type expression syntax doesn’t allow for declaring properties.
If we want to describe something callable with properties, we can write a
call signature
in an object type:
ts
type
DescribableFunction
= {
description
:
string
;
(
someArg
:
number
):
boolean
;
};
function
doSomething
(
fn
:
DescribableFunction
) {
console
.
log
(
fn
.
description
+
" returned "
+
fn
(
6
));
}
function
myFunc
(
someArg
:
number
) {
return
someArg
>
3
;
}
myFunc
.
description
=
"default description"
;
doSomething
(
myFunc
);
Try
Note that the syntax is slightly different compared to a function type expression - use
:
between the parameter list and the return type rather than
=>
.
Construct Signatures
JavaScript functions can also be invoked with the
new
operator.
TypeScript refers to these as
constructors
because they usually create a new object.
You can write a
construct signature
by adding the
new
keyword in front of a call signature:
ts
type
SomeConstructor
= {
new
(
s
:
string
):
SomeObject
;
};
function
fn
(
ctor
:
SomeConstructor
) {
return
new
ctor
(
"hello"
);
}
Try
Some objects, like JavaScript’s
Date
object, can be called with or without
new
.
You can combine call and construct signatures in the same type arbitrarily:
ts
interface
CallOrConstruct
{
(
n
?:
number
):
string
;
new
(
s
:
string
):
Date
;
}
function
fn
(
ctor
:
CallOrConstruct
) {
// Passing an argument of type `number` to `ctor` matches it against
// the first definition in the `CallOrConstruct` interface.
console
.
log
(
ctor
(
10
));
(parameter) ctor: CallOrConstruct
(n?: number) => string
// Similarly, passing an argument of type `string` to `ctor` matches it
// against the second definition in the `CallOrConstruct` interface.
console
.
log
(
new
ctor
(
"10"
));
(parameter) ctor: CallOrConstruct
new (s: string) => Date
}
fn
(
Date
);
Try
Generic Functions
It’s common to write a function where the types of the input relate to the type of the output, or where the types of two inputs are related in some way.
Let’s consider for a moment a function that returns the first element of an array:
ts
function
firstElement
(
arr
:
any
[]) {
return
arr
[
0
];
}
Try
This function does its job, but unfortunately has the return type
any
.
It’d be better if the function returned the type of the array element.
In TypeScript,
generics
are used when we want to describe a correspondence between two values.
We do this by declaring a
type parameter
in the function signature:
ts
function
firstElement
<
Type
>(
arr
:
Type
[]):
Type
|
undefined
{
return
arr
[
0
];
}
Try
By adding a type parameter
Type
to this function and using it in two places, we’ve created a link between the input of the function (the array) and the output (the return value).
Now when we call it, a more specific type comes out:
ts
// s is of type 'string'
const
s
=
firstElement
([
"a"
,
"b"
,
"c"
]);
// n is of type 'number'
const
n
=
firstElement
([
1
,
2
,
3
]);
// u is of type undefined
const
u
=
firstElement
([]);
Try
Inference
Note that we didn’t have to specify
Type
in this sample.
The type was
inferred
- chosen automatically - by TypeScript.
We can use multiple type parameters as well.
For example, a standalone version of
map
would look like this:
ts
function
map
<
Input
,
Output
>(
arr
:
Input
[],
func
: (
arg
:
Input
)
=>
Output
):
Output
[] {
return
arr
.
map
(
func
);
}
// Parameter 'n' is of type 'string'
// 'parsed' is of type 'number[]'
const
parsed
=
map
([
"1"
,
"2"
,
"3"
], (
n
)
=>
parseInt
(
n
));
Try
Note that in this example, TypeScript could infer both the type of the
Input
type parameter (from the given
string
array), as well as the
Output
type parameter based on the return value of the function expression (
number
).
Constraints
We’ve written some generic functions that can work on
any
kind of value.
Sometimes we want to relate two values, but can only operate on a certain subset of values.
In this case, we can use a
constraint
to limit the kinds of types that a type parameter can accept.
Let’s write a function that returns the longer of two values.
To do this, we need a
length
property that’s a number.
We
constrain
the type parameter to that type by writing an
extends
clause:
ts
function
longest
<
Type
extends
{
length
:
number
}>(
a
:
Type
,
b
:
Type
) {
if
(
a
.
length
>=
b
.
length
) {
return
a
;
}
else
{
return
b
;
}
}
// longerArray is of type 'number[]'
const
longerArray
=
longest
([
1
,
2
], [
1
,
2
,
3
]);
// longerString is of type 'alice' | 'bob'
const
longerString
=
longest
(
"alice"
,
"bob"
);
// Error! Numbers don't have a 'length' property
const
notOK
=
longest
(
10
,
100
);
Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.
2345
Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.
Try
There are a few interesting things to note in this example.
We allowed TypeScript to
infer
the return type of
longest
.
Return type inference also works on generic functions.
Because we constrained
Type
to
{ length: number }
, we were allowed to access the
.length
property of the
a
and
b
parameters.
Without the type constraint, we wouldn’t be able to access those properties because the values might have been some other type without a length property.
The types of
longerArray
and
longerString
were inferred based on the arguments.
Remember, generics are all about relating two or more values with the same type!
Finally, just as we’d like, the call to
longest(10, 100)
is rejected because the
number
type doesn’t have a
.length
property.
Working with Constrained Values
Here’s a common error when working with generic constraints:
ts
function
minimumLength
<
Type
extends
{
length
:
number
}>(
obj
:
Type
,
minimum
:
number
):
Type
{
if
(
obj
.
length
>=
minimum
) {
return
obj
;
}
else
{
return
{
length
:
minimum
};
Type '{ length: number; }' is not assignable to type 'Type'.
'{ length: number; }' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint '{ length: number; }'.
2322
Type '{ length: number; }' is not assignable to type 'Type'.
'{ length: number; }' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint '{ length: number; }'.
}
}
Try
It might look like this function is OK -
Type
is constrained to
{ length: number }
, and the function either returns
Type
or a value matching that constraint.
The problem is that the function promises to return the
same
kind of object as was passed in, not just
some
object matching the constraint.
If this code were legal, you could write code that definitely wouldn’t work:
ts
// 'arr' gets value { length: 6 }
const
arr
=
minimumLength
([
1
,
2
,
3
],
6
);
// and crashes here because arrays have
// a 'slice' method, but not the returned object!
console
.
log
(
arr
.
slice
(
0
));
Try
Specifying Type Arguments
TypeScript can usually infer the intended type arguments in a generic call, but not always.
For example, let’s say you wrote a function to combine two arrays:
ts
function
combine
<
Type
>(
arr1
:
Type
[],
arr2
:
Type
[]):
Type
[] {
return
arr1
.
concat
(
arr2
);
}
Try
Normally it would be an error to call this function with mismatched arrays:
ts
const
arr
=
combine
([
1
,
2
,
3
], [
"hello"
]);
Type 'string' is not assignable to type 'number'.
2322
Type 'string' is not assignable to type 'number'.
Try
If you intended to do this, however, you could manually specify
Type
:
ts
const
arr
=
combine
<
string
|
number
>([
1
,
2
,
3
], [
"hello"
]);
Try
Guidelines for Writing Good Generic Functions
Writing generic functions is fun, and it can be easy to get carried away with type parameters.
Having too many type parameters or using constraints where they aren’t needed can make inference less successful, frustrating callers of your function.
Push Type Parameters Down
Here are two ways of writing a function that appear similar:
ts
function
firstElement1
<
Type
>(
arr
:
Type
[]) {
return
arr
[
0
];
}
function
firstElement2
<
Type
extends
any
[]>(
arr
:
Type
) {
return
arr
[
0
];
}
// a: number (good)
const
a
=
firstElement1
([
1
,
2
,
3
]);
// b: any (bad)
const
b
=
firstElement2
([
1
,
2
,
3
]);
Try
These might seem identical at first glance, but
firstElement1
is a much better way to write this function.
Its inferred return type is
Type
, but
firstElement2
’s inferred return type is
any
because TypeScript has to resolve the
arr[0]
expression using the constraint type, rather than “waiting” to resolve the element during a call.
Rule
: When possible, use the type parameter itself rather than constraining it
Use Fewer Type Parameters
Here’s another pair of similar functions:
ts
function
filter1
<
Type
>(
arr
:
Type
[],
func
: (
arg
:
Type
)
=>
boolean
):
Type
[] {
return
arr
.
filter
(
func
);
}
function
filter2
<
Type
,
Func
extends
(
arg
:
Type
)
=>
boolean
>(
arr
:
Type
[],
func
:
Func
):
Type
[] {
return
arr
.
filter
(
func
);
}
Try
We’ve created a type parameter
Func
that
doesn’t relate two values
.
That’s always a red flag, because it means callers wanting to specify type arguments have to manually specify an extra type argument for no reason.
Func
doesn’t do anything but make the function harder to read and reason about!
Rule
: Always use as few type parameters as possible
Type Parameters Should Appear Twice
Sometimes we forget that a function might not need to be generic:
ts
function
greet
<
Str
extends
string
>(
s
:
Str
) {
console
.
log
(
"Hello, "
+
s
);
}
greet
(
"world"
);
Try
We could just as easily have written a simpler version:
ts
function
greet
(
s
:
string
) {
console
.
log
(
"Hello, "
+
s
);
}
Try
Remember, type parameters are for
relating the types of multiple values
.
If a type parameter is only used once in the function signature, it’s not relating anything.
This includes the inferred return type; for example, if
Str
was part of the inferred return type of
greet
, it would be relating the argument and return types, so would be used
twice
despite appearing only once in the written code.
Rule
: If a type parameter only appears in one location, strongly reconsider if you actually need it
Optional Parameters
Functions in JavaScript often take a variable number of arguments.
For example, the
toFixed
method of
number
takes an optional digit count:
ts
function
f
(
n
:
number
) {
console
.
log
(
n
.
toFixed
());
// 0 arguments
console
.
log
(
n
.
toFixed
(
3
));
// 1 argument
}
Try
We can model this in TypeScript by marking the parameter as
optional
with
?
:
ts
function
f
(
x
?:
number
) {
// ...
}
f
();
// OK
f
(
10
);
// OK
Try
Although the parameter is specified as type
number
, the
x
parameter will actually have the type
number | undefined
because unspecified parameters in JavaScript get the value
undefined
.
You can also provide a parameter
default
:
ts
function
f
(
x
=
10
) {
// ...
}
Try
Now in the body of
f
,
x
will have type
number
because any
undefined
argument will be replaced with
10
.
Note that when a parameter is optional, callers can always pass
undefined
, as this simply simulates a “missing” argument:
ts
// All OK
f
();
f
(
10
);
f
(
undefined
);
Try
Optional Parameters in Callbacks
Once you’ve learned about optional parameters and function type expressions, it’s very easy to make the following mistakes when writing functions that invoke callbacks:
ts
function
myForEach
(
arr
:
any
[],
callback
: (
arg
:
any
,
index
?:
number
)
=>
void
) {
for
(
let
i
=
0
;
i
<
arr
.
length
;
i
++) {
callback
(
arr
[
i
],
i
);
}
}
Try
What people usually intend when writing
index?
as an optional parameter is that they want both of these calls to be legal:
ts
myForEach
([
1
,
2
,
3
], (
a
)
=>
console
.
log
(
a
));
myForEach
([
1
,
2
,
3
], (
a
,
i
)
=>
console
.
log
(
a
,
i
));
Try
What this
actually
means is that
callback
might get invoked with one argument
.
In other words, the function definition says that the implementation might look like this:
ts
function
myForEach
(
arr
:
any
[],
callback
: (
arg
:
any
,
index
?:
number
)
=>
void
) {
for
(
let
i
=
0
;
i
<
arr
.
length
;
i
++) {
// I don't feel like providing the index today
callback
(
arr
[
i
]);
}
}
Try
In turn, TypeScript will enforce this meaning and issue errors that aren’t really possible:
ts
myForEach
([
1
,
2
,
3
], (
a
,
i
)
=>
{
console
.
log
(
i
.
toFixed
());
'i' is possibly 'undefined'.
18048
'i' is possibly 'undefined'.
});
Try
In JavaScript, if you call a function with more arguments than there are parameters, the extra arguments are simply ignored.
TypeScript behaves the same way.
Functions with fewer parameters (of the same types) can always take the place of functions with more parameters.
Rule
: When writing a function type for a callback,
never
write an optional parameter unless you intend to
call
the function without passing that argument
Function Overloads
Some JavaScript functions can be called in a variety of argument counts and types.
For example, you might write a function to produce a
Date
that takes either a timestamp (one argument) or a month/day/year specification (three arguments).
In TypeScript, we can specify a function that can be called in different ways by writing
overload signatures
.
To do this, write some number of function signatures (usually two or more), followed by the body of the function:
ts
function
makeDate
(
timestamp
:
number
):
Date
;
function
makeDate
(
m
:
number
,
d
:
number
,
y
:
number
):
Date
;
function
makeDate
(
mOrTimestamp
:
number
,
d
?:
number
,
y
?:
number
):
Date
{
if
(
d
!==
undefined
&&
y
!==
undefined
) {
return
new
Date
(
y
,
mOrTimestamp
,
d
);
}
else
{
return
new
Date
(
mOrTimestamp
);
}
}
const
d1
=
makeDate
(
12345678
);
const
d2
=
makeDate
(
5
,
5
,
5
);
const
d3
=
makeDate
(
1
,
3
);
No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
2575
No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
Try
In this example, we wrote two overloads: one accepting one argument, and another accepting three arguments.
These first two signatures are called the
overload signatures
.
Then, we wrote a function implementation with a compatible signature.
Functions have an
implementation
signature, but this signature can’t be called directly.
Even though we wrote a function with two optional parameters after the required one, it can’t be called with two parameters!
Overload Signatures and the Implementation Signature
This is a common source of confusion.
Often people will write code like this and not understand why there is an error:
ts
function
fn
(
x
:
string
):
void
;
function
fn
() {
// ...
}
// Expected to be able to call with zero arguments
fn
();
Expected 1 arguments, but got 0.
2554
Expected 1 arguments, but got 0.
Try
Again, the signature used to write the function body can’t be “seen” from the outside.
The signature of the
implementation
is not visible from the outside.
When writing an overloaded function, you should always have
two
or more signatures above the implementation of the function.
The implementation signature must also be
compatible
with the overload signatures.
For example, these functions have errors because the implementation signature doesn’t match the overloads in a correct way:
ts
function
fn
(
x
:
boolean
):
void
;
// Argument type isn't right
function
fn
(
x
:
string
):
void
;
This overload signature is not compatible with its implementation signature.
2394
This overload signature is not compatible with its implementation signature.
function
fn
(
x
:
boolean
) {}
Try
ts
function
fn
(
x
:
string
):
string
;
// Return type isn't right
function
fn
(
x
:
number
):
boolean
;
This overload signature is not compatible with its implementation signature.
2394
This overload signature is not compatible with its implementation signature.
function
fn
(
x
:
string
|
number
) {
return
"oops"
;
}
Try
Writing Good Overloads
Like generics, there are a few guidelines you should follow when using function overloads.
Following these principles will make your function easier to call, easier to understand, and easier to implement.
Let’s consider a function that returns the length of a string or an array:
ts
function
len
(
s
:
string
):
number
;
function
len
(
arr
:
any
[]):
number
;
function
len
(
x
:
any
) {
return
x
.
length
;
}
Try
This function is fine; we can invoke it with strings or arrays.
However, we can’t invoke it with a value that might be a string
or
an array, because TypeScript can only resolve a function call to a single overload:
ts
len
(
""
);
// OK
len
([
0
]);
// OK
len
(
Math
.
random
() >
0.5
?
"hello"
: [
0
]);
No overload matches this call.
Overload 1 of 2, '(s: string): number', gave the following error.
Argument of type 'number[] | "hello"' is not assignable to parameter of type 'string'.
Type 'number[]' is not assignable to type 'string'.
Overload 2 of 2, '(arr: any[]): number', gave the following error.
Argument of type 'number[] | "hello"' is not assignable to parameter of type 'any[]'.
Type 'string' is not assignable to type 'any[]'.
2769
No overload matches this call.
Overload 1 of 2, '(s: string): number', gave the following error.
Argument of type 'number[] | "hello"' is not assignable to parameter of type 'string'.
Type 'number[]' is not assignable to type 'string'.
Overload 2 of 2, '(arr: any[]): number', gave the following error.
Argument of type 'number[] | "hello"' is not assignable to parameter of type 'any[]'.
Type 'string' is not assignable to type 'any[]'.
Try
Because both overloads have the same argument count and same return type, we can instead write a non-overloaded version of the function:
ts
function
len
(
x
:
any
[] |
string
) {
return
x
.
length
;
}
Try
This is much better!
Callers can invoke this with either sort of value, and as an added bonus, we don’t have to figure out a correct implementation signature.
Always prefer parameters with union types instead of overloads when possible
Declaring
this
in a Function
TypeScript will infer what the
this
should be in a function via code flow analysis, for example in the following:
ts
const
user
= {
id
:
123
,
admin
:
false
,
becomeAdmin
:
function
() {
this
.
admin
=
true
;
},
};
Try
TypeScript understands that the function
user.becomeAdmin
has a corresponding
this
which is the outer object
user
.
this
,
heh
, can be enough for a lot of cases, but there are a lot of cases where you need more control over what object
this
represents. The JavaScript specification states that you cannot have a parameter called
this
, and so TypeScript uses that syntax space to let you declare the type for
this
in the function body.
ts
interface
DB
{
filterUsers
(
filter
: (
this
:
User
)
=>
boolean
):
User
[];
}
const
db
=
getDB
();
const
admins
=
db
.
filterUsers
(
function
(
this
:
User
) {
return
this
.
admin
;
});
Try
This pattern is common with callback-style APIs, where another object typically controls when your function is called. Note that you need to use
function
and not arrow functions to get this behavior:
ts
interface
DB
{
filterUsers
(
filter
: (
this
:
User
)
=>
boolean
):
User
[];
}
const
db
=
getDB
();
const
admins
=
db
.
filterUsers
(()
=>
this
.
admin
);
The containing arrow function captures the global value of 'this'.Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
7041
7017
The containing arrow function captures the global value of 'this'.Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
Try
Other Types to Know About
There are some additional types you’ll want to recognize that appear often when working with function types.
Like all types, you can use them everywhere, but these are especially relevant in the context of functions.
void
void
represents the return value of functions which don’t return a value.
It’s the inferred type any time a function doesn’t have any
return
statements, or doesn’t return any explicit value from those return statements:
ts
// The inferred return type is void
function
noop
() {
return
;
}
Try
In JavaScript, a function that doesn’t return any value will implicitly return the value
undefined
.
However,
void
and
undefined
are not the same thing in TypeScript.
There are further details at the end of this chapter.
void
is not the same as
undefined
.
object
The special type
object
refers to any value that isn’t a primitive (
string
,
number
,
bigint
,
boolean
,
symbol
,
null
, or
undefined
).
This is different from the
empty object type
{ }
, and also different from the global type
Object
.
It’s very likely you will never use
Object
.
object
is not
Object
.
Always
use
object
!
Note that in JavaScript, function values are objects: They have properties, have
Object.prototype
in their prototype chain, are
instanceof Object
, you can call
Object.keys
on them, and so on.
For this reason, function types are considered to be
object
s in TypeScript.
unknown
The
unknown
type represents
any
value.
This is similar to the
any
type, but is safer because it’s not legal to do anything with an
unknown
value:
ts
function
f1
(
a
:
any
) {
a
.
b
();
// OK
}
function
f2
(
a
:
unknown
) {
a
.
b
();
'a' is of type 'unknown'.
18046
'a' is of type 'unknown'.
}
Try
This is useful when describing function types because you can describe functions that accept any value without having
any
values in your function body.
Conversely, you can describe a function that returns a value of unknown type:
ts
function
safeParse
(
s
:
string
):
unknown
{
return
JSON
.
parse
(
s
);
}
// Need to be careful with 'obj'!
const
obj
=
safeParse
(
someRandomString
);
Try
never
Some functions
never
return a value:
ts
function
fail
(
msg
:
string
):
never
{
throw
new
Error
(
msg
);
}
Try
The
never
type represents values which are
never
observed.
In a return type, this means that the function throws an exception or terminates execution of the program.
never
also appears when TypeScript determines there’s nothing left in a union.
ts
function
fn
(
x
:
string
|
number
) {
if
(
typeof
x
===
"string"
) {
// do something
}
else
if
(
typeof
x
===
"number"
) {
// do something else
}
else
{
x
;
// has type 'never'!
}
}
Try
Function
The global type
Function
describes properties like
bind
,
call
,
apply
, and others present on all function values in JavaScript.
It also has the special property that values of type
Function
can always be called; these calls return
any
:
ts
function
doSomething
(
f
:
Function
) {
return
f
(
1
,
2
,
3
);
}
Try
This is an
untyped function call
and is generally best avoided because of the unsafe
any
return type.
If you need to accept an arbitrary function but don’t intend to call it, the type
() => void
is generally safer.
Rest Parameters and Arguments
Background Reading:
Rest Parameters
Spread Syntax
Rest Parameters
In addition to using optional parameters or overloads to make functions that can accept a variety of fixed argument counts, we can also define functions that take an
unbounded
number of arguments using
rest parameters
.
A rest parameter appears after all other parameters, and uses the
...
syntax:
ts
function
multiply
(
n
:
number
, ...
m
:
number
[]) {
return
m
.
map
((
x
)
=>
n
*
x
);
}
// 'a' gets value [10, 20, 30, 40]
const
a
=
multiply
(
10
,
1
,
2
,
3
,
4
);
Try
In TypeScript, the type annotation on these parameters is implicitly
any[]
instead of
any
, and any type annotation given must be of the form
Array<T>
or
T[]
, or a tuple type (which we’ll learn about later).
Rest Arguments
Conversely, we can
provide
a variable number of arguments from an iterable object (for example, an array) using the spread syntax.
For example, the
push
method of arrays takes any number of arguments:
ts
const
arr1
= [
1
,
2
,
3
];
const
arr2
= [
4
,
5
,
6
];
arr1
.
push
(...
arr2
);
Try
Note that in general, TypeScript does not assume that arrays are immutable.
This can lead to some surprising behavior:
ts
// Inferred type is number[] -- "an array with zero or more numbers",
// not specifically two numbers
const
args
= [
8
,
5
];
const
angle
=
Math
.
atan2
(...
args
);
A spread argument must either have a tuple type or be passed to a rest parameter.
2556
A spread argument must either have a tuple type or be passed to a rest parameter.
Try
The best fix for this situation depends a bit on your code, but in general a
const
context is the most straightforward solution:
ts
// Inferred as 2-length tuple
const
args
= [
8
,
5
]
as
const
;
// OK
const
angle
=
Math
.
atan2
(...
args
);
Try
Using rest arguments may require turning on
downlevelIteration
when targeting older runtimes.
Parameter Destructuring
Background Reading:
Destructuring Assignment
You can use parameter destructuring to conveniently unpack objects provided as an argument into one or more local variables in the function body.
In JavaScript, it looks like this:
js
function
sum
({
a
,
b
,
c
}) {
console
.
log
(
a
+
b
+
c
);
}
sum
({
a:
10
,
b:
3
,
c:
9
});
The type annotation for the object goes after the destructuring syntax:
ts
function
sum
({
a
,
b
,
c
}: {
a
:
number
;
b
:
number
;
c
:
number
}) {
console
.
log
(
a
+
b
+
c
);
}
Try
This can look a bit verbose, but you can use a named type here as well:
ts
// Same as prior example
type
ABC
= {
a
:
number
;
b
:
number
;
c
:
number
};
function
sum
({
a
,
b
,
c
}:
ABC
) {
console
.
log
(
a
+
b
+
c
);
}
Try
Assignability of Functions
Return type
void
The
void
return type for functions can produce some unusual, but expected behavior.
Contextual typing with a return type of
void
does
not
force functions to
not
return something. Another way to say this is a contextual function type with a
void
return type (
type voidFunc = () => void
), when implemented, can return
any
other value, but it will be ignored.
Thus, the following implementations of the type
() => void
are valid:
ts
type
voidFunc
= ()
=>
void
;
const
f1
:
voidFunc
= ()
=>
{
return
true
;
};
const
f2
:
voidFunc
= ()
=>
true
;
const
f3
:
voidFunc
=
function
() {
return
true
;
};
Try
And when the return value of one of these functions is assigned to another variable, it will retain the type of
void
:
ts
const
v1
=
f1
();
const
v2
=
f2
();
const
v3
=
f3
();
Try
This behavior exists so that the following code is valid even though
Array.prototype.push
returns a number and the
Array.prototype.forEach
method expects a function with a return type of
void
.
ts
const
src
= [
1
,
2
,
3
];
const
dst
= [
0
];
src
.
forEach
((
el
)
=>
dst
.
push
(
el
));
Try
There is one other special case to be aware of, when a literal function definition has a
void
return type, that function must
not
return anything.
ts
function
f2
():
void
{
// @ts-expect-error
return
true
;
}
const
f3
=
function
():
void
{
// @ts-expect-error
return
true
;
};
Try
For more on
void
please refer to these other documentation entries:
FAQ - “Why are functions returning non-void assignable to function returning void?” |
| Markdown | [Skip to main content](https://www.typescriptlang.org/docs/handbook/2/functions.html#handbook-content)
[TypeScript](https://www.typescriptlang.org/)
- [Download](https://www.typescriptlang.org/download/)
- [Docs](https://www.typescriptlang.org/docs/)
- [Handbook](https://www.typescriptlang.org/docs/handbook/intro.html)
- [Community](https://www.typescriptlang.org/community/)
- [Playground](https://www.typescriptlang.org/play/)
- [Tools](https://www.typescriptlang.org/tools/)
[in En](https://www.typescriptlang.org/docs/handbook/2/functions.html)
Was this page helpful?
- Get Started
- [TS for the New Programmer](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html)
- [TypeScript for JS Programmers](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html)
- [TS for Java/C\# Programmers](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-oop.html)
- [TS for Functional Programmers](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html)
- [TypeScript Tooling in 5 minutes](https://www.typescriptlang.org/docs/handbook/typescript-tooling-in-5-minutes.html)
- Handbook
- [The TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html)
- [The Basics](https://www.typescriptlang.org/docs/handbook/2/basic-types.html)
- [Everyday Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)
- [Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html)
- [More on Functions](https://www.typescriptlang.org/docs/handbook/2/functions.html)
- [Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html)
- Type Manipulation
- [Creating Types from Types](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html)
- [Generics](https://www.typescriptlang.org/docs/handbook/2/generics.html)
- [Keyof Type Operator](https://www.typescriptlang.org/docs/handbook/2/keyof-types.html)
- [Typeof Type Operator](https://www.typescriptlang.org/docs/handbook/2/typeof-types.html)
- [Indexed Access Types](https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html)
- [Conditional Types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html)
- [Mapped Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html)
- [Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html)
- [Classes](https://www.typescriptlang.org/docs/handbook/2/classes.html)
- [Modules](https://www.typescriptlang.org/docs/handbook/2/modules.html)
- Reference
- [Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html)
- [Cheat Sheets](https://www.typescriptlang.org/cheatsheets/)
- [Decorators](https://www.typescriptlang.org/docs/handbook/decorators.html)
- [Declaration Merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html)
- [Enums](https://www.typescriptlang.org/docs/handbook/enums.html)
- [Iterators and Generators](https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html)
- [JSX](https://www.typescriptlang.org/docs/handbook/jsx.html)
- [Mixins](https://www.typescriptlang.org/docs/handbook/mixins.html)
- [Namespaces](https://www.typescriptlang.org/docs/handbook/namespaces.html)
- [Namespaces and Modules](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html)
- [Symbols](https://www.typescriptlang.org/docs/handbook/symbols.html)
- [Triple-Slash Directives](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html)
- [Type Compatibility](https://www.typescriptlang.org/docs/handbook/type-compatibility.html)
- [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html)
- [Variable Declaration](https://www.typescriptlang.org/docs/handbook/variable-declarations.html)
- Modules Reference
- [Introduction](https://www.typescriptlang.org/docs/handbook/modules/introduction.html)
- [Theory](https://www.typescriptlang.org/docs/handbook/modules/theory.html)
- Guides
- [Choosing Compiler Options](https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html)
- [Reference](https://www.typescriptlang.org/docs/handbook/modules/reference.html)
- Appendices
- [ESM/CJS Interoperability](https://www.typescriptlang.org/docs/handbook/modules/appendices/esm-cjs-interop.html)
- Tutorials
- [ASP.NET Core](https://www.typescriptlang.org/docs/handbook/asp-net-core.html)
- [Gulp](https://www.typescriptlang.org/docs/handbook/gulp.html)
- [DOM Manipulation](https://www.typescriptlang.org/docs/handbook/dom-manipulation.html)
- [Migrating from JavaScript](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html)
- [Using Babel with TypeScript](https://www.typescriptlang.org/docs/handbook/babel-with-typescript.html)
- What's New
- [TypeScript 6.0](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html)
- [TypeScript 5.9](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-9.html)
- [TypeScript 5.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html)
- [TypeScript 5.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-7.html)
- [TypeScript 5.6](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html)
- [TypeScript 5.5](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html)
- [TypeScript 5.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-4.html)
- [TypeScript 5.3](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-3.html)
- [TypeScript 5.2](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html)
- [TypeScript 5.1](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-1.html)
- [TypeScript 5.0](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html)
- [TypeScript 4.9](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html)
- [TypeScript 4.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-8.html)
- [TypeScript 4.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html)
- [TypeScript 4.6](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-6.html)
- [TypeScript 4.5](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html)
- [TypeScript 4.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-4.html)
- [TypeScript 4.3](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html)
- [TypeScript 4.2](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-2.html)
- [TypeScript 4.1](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html)
- [TypeScript 4.0](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html)
- [TypeScript 3.9](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html)
- [TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html)
- [TypeScript 3.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html)
- [TypeScript 3.6](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html)
- [TypeScript 3.5](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html)
- [TypeScript 3.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html)
- [TypeScript 3.3](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-3.html)
- [TypeScript 3.2](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html)
- [TypeScript 3.1](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html)
- [TypeScript 3.0](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html)
- [TypeScript 2.9](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html)
- [TypeScript 2.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html)
- [TypeScript 2.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html)
- [TypeScript 2.6](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html)
- [TypeScript 2.5](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-5.html)
- [TypeScript 2.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html)
- [TypeScript 2.3](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html)
- [TypeScript 2.2](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html)
- [TypeScript 2.1](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html)
- [TypeScript 2.0](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html)
- [TypeScript 1.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-8.html)
- [TypeScript 1.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-7.html)
- [TypeScript 1.6](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-6.html)
- [TypeScript 1.5](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-5.html)
- [TypeScript 1.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-4.html)
- [TypeScript 1.3](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-3.html)
- [TypeScript 1.1](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-1.html)
- Declaration Files
- [Introduction](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html)
- [Declaration Reference](https://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html)
- [Library Structures](https://www.typescriptlang.org/docs/handbook/declaration-files/library-structures.html)
- .d.ts Templates
- [Modules .d.ts](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html)
- [Module: Plugin](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-plugin-d-ts.html)
- [Module: Class](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-class-d-ts.html)
- [Module: Function](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-function-d-ts.html)
- [Global .d.ts](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-d-ts.html)
- [Global: Modifying Module](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html)
- [Do's and Don'ts](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html)
- [Deep Dive](https://www.typescriptlang.org/docs/handbook/declaration-files/deep-dive.html)
- [Publishing](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html)
- [Consumption](https://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html)
- JavaScript
- [JS Projects Utilizing TypeScript](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html)
- [Type Checking JavaScript Files](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html)
- [JSDoc Reference](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html)
- [Creating .d.ts Files from .js files](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html)
- Project Configuration
- [What is a tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)
- [Compiler Options in MSBuild](https://www.typescriptlang.org/docs/handbook/compiler-options-in-msbuild.html)
- [TSConfig Reference](https://www.typescriptlang.org/tsconfig/)
- [tsc CLI Options](https://www.typescriptlang.org/docs/handbook/compiler-options.html)
- [Project References](https://www.typescriptlang.org/docs/handbook/project-references.html)
- [Integrating with Build Tools](https://www.typescriptlang.org/docs/handbook/integrating-with-build-tools.html)
- [Configuring Watch](https://www.typescriptlang.org/docs/handbook/configuring-watch.html)
- [Nightly Builds](https://www.typescriptlang.org/docs/handbook/nightly-builds.html)
# More on Functions
Functions are the basic building block of any application, whether they’re local functions, imported from another module, or methods on a class. They’re also values, and just like other values, TypeScript has many ways to describe how functions can be called. Let’s learn about how to write types that describe functions.
## Function Type Expressions
The simplest way to describe a function is with a *function type expression*. These types are syntactically similar to arrow functions:
```
tsfunction greeter ( fn : ( a : string) => void) {fn ("Hello, World");}function printToConsole ( s : string) {console . log ( s );}greeter ( printToConsole );Try
```
The syntax `(a: string) => void` means “a function with one parameter, named `a`, of type `string`, that doesn’t have a return value”. Just like with function declarations, if a parameter type isn’t specified, it’s implicitly `any`.
> Note that the parameter name is **required**. The function type `(string) => void` means “a function with a parameter named `string` of type `any`“\!
Of course, we can use a type alias to name a function type:
```
tstype GreetFunction = ( a : string) => void;function greeter ( fn : GreetFunction ) {// ...}Try
```
## Call Signatures
In JavaScript, functions can have properties in addition to being callable. However, the function type expression syntax doesn’t allow for declaring properties. If we want to describe something callable with properties, we can write a *call signature* in an object type:
```
tstype DescribableFunction = {description : string;( someArg : number): boolean;};function doSomething ( fn : DescribableFunction ) {console . log ( fn . description + " returned " + fn (6));}function myFunc ( someArg : number) {return someArg > 3;}myFunc . description = "default description";doSomething ( myFunc );Try
```
Note that the syntax is slightly different compared to a function type expression - use `:` between the parameter list and the return type rather than `=>`.
## Construct Signatures
JavaScript functions can also be invoked with the `new` operator. TypeScript refers to these as *constructors* because they usually create a new object. You can write a *construct signature* by adding the `new` keyword in front of a call signature:
```
tstype SomeConstructor = {new ( s : string): SomeObject ;};function fn ( ctor : SomeConstructor ) {return new ctor ("hello");}Try
```
Some objects, like JavaScript’s `Date` object, can be called with or without `new`. You can combine call and construct signatures in the same type arbitrarily:
```
tsinterface CallOrConstruct {( n ?: number): string;new ( s : string): Date ;}function fn ( ctor : CallOrConstruct ) {// Passing an argument of type `number` to `ctor` matches it against// the first definition in the `CallOrConstruct` interface.console . log ( ctor (10));(parameter) ctor: CallOrConstruct (n?: number) => string// Similarly, passing an argument of type `string` to `ctor` matches it// against the second definition in the `CallOrConstruct` interface.console . log (new ctor ("10"));(parameter) ctor: CallOrConstruct new (s: string) => Date}fn ( Date );Try
```
## Generic Functions
It’s common to write a function where the types of the input relate to the type of the output, or where the types of two inputs are related in some way. Let’s consider for a moment a function that returns the first element of an array:
```
tsfunction firstElement ( arr : any[]) {return arr [0];}Try
```
This function does its job, but unfortunately has the return type `any`. It’d be better if the function returned the type of the array element.
In TypeScript, *generics* are used when we want to describe a correspondence between two values. We do this by declaring a *type parameter* in the function signature:
```
tsfunction firstElement < Type >( arr : Type []): Type | undefined {return arr [0];}Try
```
By adding a type parameter `Type` to this function and using it in two places, we’ve created a link between the input of the function (the array) and the output (the return value). Now when we call it, a more specific type comes out:
```
ts// s is of type 'string'const s = firstElement (["a", "b", "c"]);// n is of type 'number'const n = firstElement ([1, 2, 3]);// u is of type undefinedconst u = firstElement ([]);Try
```
### Inference
Note that we didn’t have to specify `Type` in this sample. The type was *inferred* - chosen automatically - by TypeScript.
We can use multiple type parameters as well. For example, a standalone version of `map` would look like this:
```
tsfunction map < Input , Output >( arr : Input [], func : ( arg : Input ) => Output ): Output [] {return arr . map ( func );}// Parameter 'n' is of type 'string'// 'parsed' is of type 'number[]'const parsed = map (["1", "2", "3"], ( n ) => parseInt ( n ));Try
```
Note that in this example, TypeScript could infer both the type of the `Input` type parameter (from the given `string` array), as well as the `Output` type parameter based on the return value of the function expression (`number`).
### Constraints
We’ve written some generic functions that can work on *any* kind of value. Sometimes we want to relate two values, but can only operate on a certain subset of values. In this case, we can use a *constraint* to limit the kinds of types that a type parameter can accept.
Let’s write a function that returns the longer of two values. To do this, we need a `length` property that’s a number. We *constrain* the type parameter to that type by writing an `extends` clause:
```
tsfunction longest < Type extends { length : number }>( a : Type , b : Type ) {if ( a . length >= b . length ) {return a ;} else {return b ;}}// longerArray is of type 'number[]'const longerArray = longest ([1, 2], [1, 2, 3]);// longerString is of type 'alice' | 'bob'const longerString = longest ("alice", "bob");// Error! Numbers don't have a 'length' propertyconst notOK = longest ( 10 , 100);Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.2345Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.Try
```
There are a few interesting things to note in this example. We allowed TypeScript to *infer* the return type of `longest`. Return type inference also works on generic functions.
Because we constrained `Type` to `{ length: number }`, we were allowed to access the `.length` property of the `a` and `b` parameters. Without the type constraint, we wouldn’t be able to access those properties because the values might have been some other type without a length property.
The types of `longerArray` and `longerString` were inferred based on the arguments. Remember, generics are all about relating two or more values with the same type\!
Finally, just as we’d like, the call to `longest(10, 100)` is rejected because the `number` type doesn’t have a `.length` property.
### Working with Constrained Values
Here’s a common error when working with generic constraints:
```
tsTry
```
It might look like this function is OK - `Type` is constrained to `{ length: number }`, and the function either returns `Type` or a value matching that constraint. The problem is that the function promises to return the *same* kind of object as was passed in, not just *some* object matching the constraint. If this code were legal, you could write code that definitely wouldn’t work:
```
ts// 'arr' gets value { length: 6 }const arr = minimumLength ([1, 2, 3], 6);// and crashes here because arrays have// a 'slice' method, but not the returned object!console . log ( arr . slice (0));Try
```
### Specifying Type Arguments
TypeScript can usually infer the intended type arguments in a generic call, but not always. For example, let’s say you wrote a function to combine two arrays:
```
tsfunction combine < Type >( arr1 : Type [], arr2 : Type []): Type [] {return arr1 . concat ( arr2 );}Try
```
Normally it would be an error to call this function with mismatched arrays:
```
tsconst arr = combine ([1, 2, 3], [ "hello" ]);Type 'string' is not assignable to type 'number'.2322Type 'string' is not assignable to type 'number'.Try
```
If you intended to do this, however, you could manually specify `Type`:
```
tsconst arr = combine <string | number>([1, 2, 3], ["hello"]);Try
```
### Guidelines for Writing Good Generic Functions
Writing generic functions is fun, and it can be easy to get carried away with type parameters. Having too many type parameters or using constraints where they aren’t needed can make inference less successful, frustrating callers of your function.
#### Push Type Parameters Down
Here are two ways of writing a function that appear similar:
```
tsfunction firstElement1 < Type >( arr : Type []) {return arr [0];}function firstElement2 < Type extends any[]>( arr : Type ) {return arr [0];}// a: number (good)const a = firstElement1 ([1, 2, 3]);// b: any (bad)const b = firstElement2 ([1, 2, 3]);Try
```
These might seem identical at first glance, but `firstElement1` is a much better way to write this function. Its inferred return type is `Type`, but `firstElement2`’s inferred return type is `any` because TypeScript has to resolve the `arr[0]` expression using the constraint type, rather than “waiting” to resolve the element during a call.
> **Rule**: When possible, use the type parameter itself rather than constraining it
#### Use Fewer Type Parameters
Here’s another pair of similar functions:
```
tsfunction filter1 < Type >( arr : Type [], func : ( arg : Type ) => boolean): Type [] {return arr . filter ( func );}function filter2 < Type , Func extends ( arg : Type ) => boolean>(arr : Type [],func : Func): Type [] {return arr . filter ( func );}Try
```
We’ve created a type parameter `Func` that *doesn’t relate two values*. That’s always a red flag, because it means callers wanting to specify type arguments have to manually specify an extra type argument for no reason. `Func` doesn’t do anything but make the function harder to read and reason about\!
> **Rule**: Always use as few type parameters as possible
#### Type Parameters Should Appear Twice
Sometimes we forget that a function might not need to be generic:
```
tsfunction greet < Str extends string>( s : Str ) {console . log ("Hello, " + s );}greet ("world");Try
```
We could just as easily have written a simpler version:
```
tsfunction greet ( s : string) {console . log ("Hello, " + s );}Try
```
Remember, type parameters are for *relating the types of multiple values*. If a type parameter is only used once in the function signature, it’s not relating anything. This includes the inferred return type; for example, if `Str` was part of the inferred return type of `greet`, it would be relating the argument and return types, so would be used *twice* despite appearing only once in the written code.
> **Rule**: If a type parameter only appears in one location, strongly reconsider if you actually need it
## Optional Parameters
Functions in JavaScript often take a variable number of arguments. For example, the `toFixed` method of `number` takes an optional digit count:
```
tsfunction f ( n : number) {console . log ( n . toFixed ()); // 0 argumentsconsole . log ( n . toFixed (3)); // 1 argument}Try
```
We can model this in TypeScript by marking the parameter as *optional* with `?`:
```
tsfunction f ( x ?: number) {// ...}f (); // OKf (10); // OKTry
```
Although the parameter is specified as type `number`, the `x` parameter will actually have the type `number | undefined` because unspecified parameters in JavaScript get the value `undefined`.
You can also provide a parameter *default*:
```
tsfunction f ( x = 10) {// ...}Try
```
Now in the body of `f`, `x` will have type `number` because any `undefined` argument will be replaced with `10`. Note that when a parameter is optional, callers can always pass `undefined`, as this simply simulates a “missing” argument:
```
ts// All OKf ();f (10);f ( undefined );Try
```
### Optional Parameters in Callbacks
Once you’ve learned about optional parameters and function type expressions, it’s very easy to make the following mistakes when writing functions that invoke callbacks:
```
tsfunction myForEach ( arr : any[], callback : ( arg : any, index ?: number) => void) {for (let i = 0; i < arr . length ; i ++) {callback ( arr [ i ], i );}}Try
```
What people usually intend when writing `index?` as an optional parameter is that they want both of these calls to be legal:
```
tsmyForEach ([1, 2, 3], ( a ) => console . log ( a ));myForEach ([1, 2, 3], ( a , i ) => console . log ( a , i ));Try
```
What this *actually* means is that *`callback` might get invoked with one argument*. In other words, the function definition says that the implementation might look like this:
```
tsfunction myForEach ( arr : any[], callback : ( arg : any, index ?: number) => void) {for (let i = 0; i < arr . length ; i ++) {// I don't feel like providing the index todaycallback ( arr [ i ]);}}Try
```
In turn, TypeScript will enforce this meaning and issue errors that aren’t really possible:
```
tsmyForEach ([1, 2, 3], ( a , i ) => {console . log ( i . toFixed ());'i' is possibly 'undefined'.18048'i' is possibly 'undefined'.});Try
```
In JavaScript, if you call a function with more arguments than there are parameters, the extra arguments are simply ignored. TypeScript behaves the same way. Functions with fewer parameters (of the same types) can always take the place of functions with more parameters.
> **Rule**: When writing a function type for a callback, *never* write an optional parameter unless you intend to *call* the function without passing that argument
## Function Overloads
Some JavaScript functions can be called in a variety of argument counts and types. For example, you might write a function to produce a `Date` that takes either a timestamp (one argument) or a month/day/year specification (three arguments).
In TypeScript, we can specify a function that can be called in different ways by writing *overload signatures*. To do this, write some number of function signatures (usually two or more), followed by the body of the function:
```
tsfunction makeDate ( timestamp : number): Date ;function makeDate ( m : number, d : number, y : number): Date ;function makeDate ( mOrTimestamp : number, d ?: number, y ?: number): Date {if ( d !== undefined && y !== undefined ) {return new Date ( y , mOrTimestamp , d );} else {return new Date ( mOrTimestamp );}}const d1 = makeDate (12345678);const d2 = makeDate (5, 5, 5);const d3 = makeDate (1, 3);No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.2575No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.Try
```
In this example, we wrote two overloads: one accepting one argument, and another accepting three arguments. These first two signatures are called the *overload signatures*.
Then, we wrote a function implementation with a compatible signature. Functions have an *implementation* signature, but this signature can’t be called directly. Even though we wrote a function with two optional parameters after the required one, it can’t be called with two parameters\!
### Overload Signatures and the Implementation Signature
This is a common source of confusion. Often people will write code like this and not understand why there is an error:
```
tsfunction fn ( x : string): void;function fn () {// ...}// Expected to be able to call with zero argumentsfn ();Expected 1 arguments, but got 0.2554Expected 1 arguments, but got 0.Try
```
Again, the signature used to write the function body can’t be “seen” from the outside.
> The signature of the *implementation* is not visible from the outside. When writing an overloaded function, you should always have *two* or more signatures above the implementation of the function.
The implementation signature must also be *compatible* with the overload signatures. For example, these functions have errors because the implementation signature doesn’t match the overloads in a correct way:
```
tsfunction fn ( x : boolean): void;// Argument type isn't rightfunction fn ( x : string): void;This overload signature is not compatible with its implementation signature.2394This overload signature is not compatible with its implementation signature.function fn ( x : boolean) {}Try
```
```
tsfunction fn ( x : string): string;// Return type isn't rightfunction fn ( x : number): boolean;This overload signature is not compatible with its implementation signature.2394This overload signature is not compatible with its implementation signature.function fn ( x : string | number) {return "oops";}Try
```
### Writing Good Overloads
Like generics, there are a few guidelines you should follow when using function overloads. Following these principles will make your function easier to call, easier to understand, and easier to implement.
Let’s consider a function that returns the length of a string or an array:
```
tsfunction len ( s : string): number;function len ( arr : any[]): number;function len ( x : any) {return x . length ;}Try
```
This function is fine; we can invoke it with strings or arrays. However, we can’t invoke it with a value that might be a string *or* an array, because TypeScript can only resolve a function call to a single overload:
```
tsTry
```
Because both overloads have the same argument count and same return type, we can instead write a non-overloaded version of the function:
```
tsfunction len ( x : any[] | string) {return x . length ;}Try
```
This is much better! Callers can invoke this with either sort of value, and as an added bonus, we don’t have to figure out a correct implementation signature.
> Always prefer parameters with union types instead of overloads when possible
## Declaring `this` in a Function
TypeScript will infer what the `this` should be in a function via code flow analysis, for example in the following:
```
tsconst user = {id : 123,admin : false,becomeAdmin : function () {this. admin = true;},};Try
```
TypeScript understands that the function `user.becomeAdmin` has a corresponding `this` which is the outer object `user`. `this`, *heh*, can be enough for a lot of cases, but there are a lot of cases where you need more control over what object `this` represents. The JavaScript specification states that you cannot have a parameter called `this`, and so TypeScript uses that syntax space to let you declare the type for `this` in the function body.
```
tsinterface DB {filterUsers ( filter : ( this : User ) => boolean): User [];}const db = getDB ();const admins = db . filterUsers (function ( this : User ) {return this. admin ;});Try
```
This pattern is common with callback-style APIs, where another object typically controls when your function is called. Note that you need to use `function` and not arrow functions to get this behavior:
```
tsTry
```
## Other Types to Know About
There are some additional types you’ll want to recognize that appear often when working with function types. Like all types, you can use them everywhere, but these are especially relevant in the context of functions.
### `void`
`void` represents the return value of functions which don’t return a value. It’s the inferred type any time a function doesn’t have any `return` statements, or doesn’t return any explicit value from those return statements:
```
ts// The inferred return type is voidfunction noop () {return;}Try
```
In JavaScript, a function that doesn’t return any value will implicitly return the value `undefined`. However, `void` and `undefined` are not the same thing in TypeScript. There are further details at the end of this chapter.
> `void` is not the same as `undefined`.
### `object`
The special type `object` refers to any value that isn’t a primitive (`string`, `number`, `bigint`, `boolean`, `symbol`, `null`, or `undefined`). This is different from the *empty object type* `{ }`, and also different from the global type `Object`. It’s very likely you will never use `Object`.
> `object` is not `Object`. **Always** use `object`\!
Note that in JavaScript, function values are objects: They have properties, have `Object.prototype` in their prototype chain, are `instanceof Object`, you can call `Object.keys` on them, and so on. For this reason, function types are considered to be `object`s in TypeScript.
### `unknown`
The `unknown` type represents *any* value. This is similar to the `any` type, but is safer because it’s not legal to do anything with an `unknown` value:
```
tsfunction f1 ( a : any) {a . b (); // OK}function f2 ( a : unknown) {a . b ();'a' is of type 'unknown'.18046'a' is of type 'unknown'.}Try
```
This is useful when describing function types because you can describe functions that accept any value without having `any` values in your function body.
Conversely, you can describe a function that returns a value of unknown type:
```
tsfunction safeParse ( s : string): unknown {return JSON . parse ( s );}// Need to be careful with 'obj'!const obj = safeParse ( someRandomString );Try
```
### `never`
Some functions *never* return a value:
```
tsfunction fail ( msg : string): never {throw new Error ( msg );}Try
```
The `never` type represents values which are *never* observed. In a return type, this means that the function throws an exception or terminates execution of the program.
`never` also appears when TypeScript determines there’s nothing left in a union.
```
tsfunction fn ( x : string | number) {if (typeof x === "string") {// do something} else if (typeof x === "number") {// do something else} else {x ; // has type 'never'!}}Try
```
### `Function`
The global type `Function` describes properties like `bind`, `call`, `apply`, and others present on all function values in JavaScript. It also has the special property that values of type `Function` can always be called; these calls return `any`:
```
tsfunction doSomething ( f : Function ) {return f (1, 2, 3);}Try
```
This is an *untyped function call* and is generally best avoided because of the unsafe `any` return type.
If you need to accept an arbitrary function but don’t intend to call it, the type `() => void` is generally safer.
## Rest Parameters and Arguments
> Background Reading:
> [Rest Parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters)
> [Spread Syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
### Rest Parameters
In addition to using optional parameters or overloads to make functions that can accept a variety of fixed argument counts, we can also define functions that take an *unbounded* number of arguments using *rest parameters*.
A rest parameter appears after all other parameters, and uses the `...` syntax:
```
tsfunction multiply ( n : number, ... m : number[]) {return m . map (( x ) => n * x );}// 'a' gets value [10, 20, 30, 40]const a = multiply (10, 1, 2, 3, 4);Try
```
In TypeScript, the type annotation on these parameters is implicitly `any[]` instead of `any`, and any type annotation given must be of the form `Array<T>` or `T[]`, or a tuple type (which we’ll learn about later).
### Rest Arguments
Conversely, we can *provide* a variable number of arguments from an iterable object (for example, an array) using the spread syntax. For example, the `push` method of arrays takes any number of arguments:
```
tsconst arr1 = [1, 2, 3];const arr2 = [4, 5, 6];arr1 . push (... arr2 );Try
```
Note that in general, TypeScript does not assume that arrays are immutable. This can lead to some surprising behavior:
```
ts// Inferred type is number[] -- "an array with zero or more numbers",// not specifically two numbersconst args = [8, 5];const angle = Math . atan2 (... args );A spread argument must either have a tuple type or be passed to a rest parameter.2556A spread argument must either have a tuple type or be passed to a rest parameter.Try
```
The best fix for this situation depends a bit on your code, but in general a `const` context is the most straightforward solution:
```
ts// Inferred as 2-length tupleconst args = [8, 5] as const ;// OKconst angle = Math . atan2 (... args );Try
```
Using rest arguments may require turning on [`downlevelIteration`](https://www.typescriptlang.org/tsconfig#downlevelIteration) when targeting older runtimes.
## Parameter Destructuring
> Background Reading:
> [Destructuring Assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
You can use parameter destructuring to conveniently unpack objects provided as an argument into one or more local variables in the function body. In JavaScript, it looks like this:
```
jsfunction sum({ a, b, c }) {console.log(a + b + c);}sum({ a: 10, b: 3, c: 9 });
```
The type annotation for the object goes after the destructuring syntax:
```
tsfunction sum ({ a , b , c }: { a : number; b : number; c : number }) {console . log ( a + b + c );}Try
```
This can look a bit verbose, but you can use a named type here as well:
```
ts// Same as prior exampletype ABC = { a : number; b : number; c : number };function sum ({ a , b , c }: ABC ) {console . log ( a + b + c );}Try
```
## Assignability of Functions
### Return type `void`
The `void` return type for functions can produce some unusual, but expected behavior.
Contextual typing with a return type of `void` does **not** force functions to **not** return something. Another way to say this is a contextual function type with a `void` return type (`type voidFunc = () => void`), when implemented, can return *any* other value, but it will be ignored.
Thus, the following implementations of the type `() => void` are valid:
```
tstype voidFunc = () => void;const f1 : voidFunc = () => {return true;};const f2 : voidFunc = () => true;const f3 : voidFunc = function () {return true;};Try
```
And when the return value of one of these functions is assigned to another variable, it will retain the type of `void`:
```
tsconst v1 = f1 ();const v2 = f2 ();const v3 = f3 ();Try
```
This behavior exists so that the following code is valid even though `Array.prototype.push` returns a number and the `Array.prototype.forEach` method expects a function with a return type of `void`.
```
tsconst src = [1, 2, 3];const dst = [0];src . forEach (( el ) => dst . push ( el ));Try
```
There is one other special case to be aware of, when a literal function definition has a `void` return type, that function must **not** return anything.
```
tsfunction f2 (): void {// @ts-expect-errorreturn true;}const f3 = function (): void {// @ts-expect-errorreturn true;};Try
```
For more on `void` please refer to these other documentation entries:
- [FAQ - “Why are functions returning non-void assignable to function returning void?”](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void)
##### On this page
- [Function Type Expressions](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-type-expressions)
- [Call Signatures](https://www.typescriptlang.org/docs/handbook/2/functions.html#call-signatures)
- [Construct Signatures](https://www.typescriptlang.org/docs/handbook/2/functions.html#construct-signatures)
- [Generic Functions](https://www.typescriptlang.org/docs/handbook/2/functions.html#generic-functions)
- [Inference](https://www.typescriptlang.org/docs/handbook/2/functions.html#inference)
- [Constraints](https://www.typescriptlang.org/docs/handbook/2/functions.html#constraints)
- [Working with Constrained Values](https://www.typescriptlang.org/docs/handbook/2/functions.html#working-with-constrained-values)
- [Specifying Type Arguments](https://www.typescriptlang.org/docs/handbook/2/functions.html#specifying-type-arguments)
- [Guidelines for Writing Good Generic Functions](https://www.typescriptlang.org/docs/handbook/2/functions.html#guidelines-for-writing-good-generic-functions)
- [Optional Parameters](https://www.typescriptlang.org/docs/handbook/2/functions.html#optional-parameters)
- [Optional Parameters in Callbacks](https://www.typescriptlang.org/docs/handbook/2/functions.html#optional-parameters-in-callbacks)
- [Function Overloads](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads)
- [Overload Signatures and the Implementation Signature](https://www.typescriptlang.org/docs/handbook/2/functions.html#overload-signatures-and-the-implementation-signature)
- [Writing Good Overloads](https://www.typescriptlang.org/docs/handbook/2/functions.html#writing-good-overloads)
- [Declaring this in a Function](https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function)
- [Other Types to Know About](https://www.typescriptlang.org/docs/handbook/2/functions.html#other-types-to-know-about)
- [void](https://www.typescriptlang.org/docs/handbook/2/functions.html#void)
- [object](https://www.typescriptlang.org/docs/handbook/2/functions.html#object)
- [unknown](https://www.typescriptlang.org/docs/handbook/2/functions.html#unknown)
- [never](https://www.typescriptlang.org/docs/handbook/2/functions.html#never)
- [Function](https://www.typescriptlang.org/docs/handbook/2/functions.html#function)
- [Rest Parameters and Arguments](https://www.typescriptlang.org/docs/handbook/2/functions.html#rest-parameters-and-arguments)
- [Rest Parameters](https://www.typescriptlang.org/docs/handbook/2/functions.html#rest-parameters)
- [Rest Arguments](https://www.typescriptlang.org/docs/handbook/2/functions.html#rest-arguments)
- [Parameter Destructuring](https://www.typescriptlang.org/docs/handbook/2/functions.html#parameter-destructuring)
- [Assignability of Functions](https://www.typescriptlang.org/docs/handbook/2/functions.html#assignability-of-functions)
- [Return type void](https://www.typescriptlang.org/docs/handbook/2/functions.html#return-type-void)
##### Is this page helpful?
Yes
No
[Previous Narrowing Understand how TypeScript uses JavaScript knowledge to reduce the amount of type syntax in your projects.](https://www.typescriptlang.org/docs/handbook/2/narrowing.html)
[Next Object Types How TypeScript describes the shapes of JavaScript objects.](https://www.typescriptlang.org/docs/handbook/2/objects.html)
The TypeScript docs are an open source project. Help us improve these pages [by sending a Pull Request](https://github.com/microsoft/TypeScript-Website/blob/v2/packages/documentation/copy/en/handbook-v2/More%20on%20Functions.md) ❤
Contributors to this page:
RC
OT
HA
JW
SB
38+
Last updated: Apr 06, 2026
### Customize
Site Colours:
Code Font:
### Popular Documentation Pages
- [Everyday Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)
All of the common types in TypeScript
- [Creating Types from Types](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html)
Techniques to make more elegant types
- [More on Functions](https://www.typescriptlang.org/docs/handbook/2/functions.html)
How to provide types to functions in JavaScript
- [More on Objects](https://www.typescriptlang.org/docs/handbook/2/objects.html)
How to provide a type shape to JavaScript objects
- [Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html)
How TypeScript infers types based on runtime behavior
- [Variable Declarations](https://www.typescriptlang.org/docs/handbook/variable-declarations.html)
How to create and type JavaScript variables
- [TypeScript in 5 minutes](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html)
An overview of building a TypeScript web app
- [TSConfig Options](https://www.typescriptlang.org/tsconfig/)
All the configuration options for a project
- [Classes](https://www.typescriptlang.org/docs/handbook/2/classes.html)
How to provide types to JavaScript ES6 classes
Made with ♥ in Redmond, Boston, SF & Dublin
[](https://www.typescriptlang.org/docs/handbook/2/functions.html)
© 2012-2026 Microsoft
[Privacy](https://go.microsoft.com/fwlink/?LinkId=521839 "Microsoft Privacy Policy")[Terms of Use](https://go.microsoft.com/fwlink/?LinkID=206977)
### Using TypeScript
- [Get Started](https://www.typescriptlang.org/docs/)
- [Download](https://www.typescriptlang.org/download/)
- [Community](https://www.typescriptlang.org/community/)
- [Playground](https://www.typescriptlang.org/play/)
- [TSConfig Ref](https://www.typescriptlang.org/tsconfig/)
- [Code Samples](https://www.typescriptlang.org/play/#show-examples)
- [Why TypeScript](https://www.typescriptlang.org/why-create-typescript/)
- [Design](https://www.typescriptlang.org/branding/)
### Community
- [Get Help](https://www.typescriptlang.org/community)
- [Blog](https://devblogs.microsoft.com/typescript/)
- [GitHub Repo](https://github.com/microsoft/TypeScript/#readme)
- [Community Chat](https://discord.gg/typescript)
- [@TypeScript](https://twitter.com/TypeScript)
- [Mastodon](https://fosstodon.org/@TypeScript)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/typescript)
- [Web Repo](https://github.com/microsoft/TypeScript-Website)
MSG |
| Readable Markdown | Functions are the basic building block of any application, whether they’re local functions, imported from another module, or methods on a class. They’re also values, and just like other values, TypeScript has many ways to describe how functions can be called. Let’s learn about how to write types that describe functions.
## Function Type Expressions
The simplest way to describe a function is with a *function type expression*. These types are syntactically similar to arrow functions:
```
tsfunction greeter(fn: (a: string) => void) {fn("Hello, World");}function printToConsole(s: string) {console.log(s);}greeter(printToConsole);Try
```
The syntax `(a: string) => void` means “a function with one parameter, named `a`, of type `string`, that doesn’t have a return value”. Just like with function declarations, if a parameter type isn’t specified, it’s implicitly `any`.
> Note that the parameter name is **required**. The function type `(string) => void` means “a function with a parameter named `string` of type `any`“\!
Of course, we can use a type alias to name a function type:
```
tstype GreetFunction = (a: string) => void;function greeter(fn: GreetFunction) {// ...}Try
```
## Call Signatures
In JavaScript, functions can have properties in addition to being callable. However, the function type expression syntax doesn’t allow for declaring properties. If we want to describe something callable with properties, we can write a *call signature* in an object type:
```
tstype DescribableFunction = {description: string;(someArg: number): boolean;};function doSomething(fn: DescribableFunction) {console.log(fn.description + " returned " + fn(6));}function myFunc(someArg: number) {return someArg > 3;}myFunc.description = "default description";doSomething(myFunc);Try
```
Note that the syntax is slightly different compared to a function type expression - use `:` between the parameter list and the return type rather than `=>`.
## Construct Signatures
JavaScript functions can also be invoked with the `new` operator. TypeScript refers to these as *constructors* because they usually create a new object. You can write a *construct signature* by adding the `new` keyword in front of a call signature:
```
tstype SomeConstructor = {new (s: string): SomeObject;};function fn(ctor: SomeConstructor) {return new ctor("hello");}Try
```
Some objects, like JavaScript’s `Date` object, can be called with or without `new`. You can combine call and construct signatures in the same type arbitrarily:
```
tsinterface CallOrConstruct {(n?: number): string;new (s: string): Date;}function fn(ctor: CallOrConstruct) {// Passing an argument of type `number` to `ctor` matches it against// the first definition in the `CallOrConstruct` interface.console.log(ctor(10));(parameter) ctor: CallOrConstruct (n?: number) => string// Similarly, passing an argument of type `string` to `ctor` matches it// against the second definition in the `CallOrConstruct` interface.console.log(new ctor("10"));(parameter) ctor: CallOrConstruct new (s: string) => Date}fn(Date);Try
```
## Generic Functions
It’s common to write a function where the types of the input relate to the type of the output, or where the types of two inputs are related in some way. Let’s consider for a moment a function that returns the first element of an array:
```
tsfunction firstElement(arr: any[]) {return arr[0];}Try
```
This function does its job, but unfortunately has the return type `any`. It’d be better if the function returned the type of the array element.
In TypeScript, *generics* are used when we want to describe a correspondence between two values. We do this by declaring a *type parameter* in the function signature:
```
tsfunction firstElement<Type>(arr: Type[]): Type | undefined {return arr[0];}Try
```
By adding a type parameter `Type` to this function and using it in two places, we’ve created a link between the input of the function (the array) and the output (the return value). Now when we call it, a more specific type comes out:
```
ts// s is of type 'string'const s = firstElement(["a", "b", "c"]);// n is of type 'number'const n = firstElement([1, 2, 3]);// u is of type undefinedconst u = firstElement([]);Try
```
### Inference
Note that we didn’t have to specify `Type` in this sample. The type was *inferred* - chosen automatically - by TypeScript.
We can use multiple type parameters as well. For example, a standalone version of `map` would look like this:
```
tsfunction map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {return arr.map(func);}// Parameter 'n' is of type 'string'// 'parsed' is of type 'number[]'const parsed = map(["1", "2", "3"], (n) => parseInt(n));Try
```
Note that in this example, TypeScript could infer both the type of the `Input` type parameter (from the given `string` array), as well as the `Output` type parameter based on the return value of the function expression (`number`).
### Constraints
We’ve written some generic functions that can work on *any* kind of value. Sometimes we want to relate two values, but can only operate on a certain subset of values. In this case, we can use a *constraint* to limit the kinds of types that a type parameter can accept.
Let’s write a function that returns the longer of two values. To do this, we need a `length` property that’s a number. We *constrain* the type parameter to that type by writing an `extends` clause:
```
tsfunction longest<Type extends { length: number }>(a: Type, b: Type) {if (a.length >= b.length) {return a;} else {return b;}}// longerArray is of type 'number[]'const longerArray = longest([1, 2], [1, 2, 3]);// longerString is of type 'alice' | 'bob'const longerString = longest("alice", "bob");// Error! Numbers don't have a 'length' propertyconst notOK = longest(10, 100);Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.2345Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.Try
```
There are a few interesting things to note in this example. We allowed TypeScript to *infer* the return type of `longest`. Return type inference also works on generic functions.
Because we constrained `Type` to `{ length: number }`, we were allowed to access the `.length` property of the `a` and `b` parameters. Without the type constraint, we wouldn’t be able to access those properties because the values might have been some other type without a length property.
The types of `longerArray` and `longerString` were inferred based on the arguments. Remember, generics are all about relating two or more values with the same type\!
Finally, just as we’d like, the call to `longest(10, 100)` is rejected because the `number` type doesn’t have a `.length` property.
### Working with Constrained Values
Here’s a common error when working with generic constraints:
```
tsTry
```
It might look like this function is OK - `Type` is constrained to `{ length: number }`, and the function either returns `Type` or a value matching that constraint. The problem is that the function promises to return the *same* kind of object as was passed in, not just *some* object matching the constraint. If this code were legal, you could write code that definitely wouldn’t work:
```
ts// 'arr' gets value { length: 6 }const arr = minimumLength([1, 2, 3], 6);// and crashes here because arrays have// a 'slice' method, but not the returned object!console.log(arr.slice(0));Try
```
### Specifying Type Arguments
TypeScript can usually infer the intended type arguments in a generic call, but not always. For example, let’s say you wrote a function to combine two arrays:
```
tsfunction combine<Type>(arr1: Type[], arr2: Type[]): Type[] {return arr1.concat(arr2);}Try
```
Normally it would be an error to call this function with mismatched arrays:
```
tsconst arr = combine([1, 2, 3], ["hello"]);Type 'string' is not assignable to type 'number'.2322Type 'string' is not assignable to type 'number'.Try
```
If you intended to do this, however, you could manually specify `Type`:
```
tsconst arr = combine<string | number>([1, 2, 3], ["hello"]);Try
```
### Guidelines for Writing Good Generic Functions
Writing generic functions is fun, and it can be easy to get carried away with type parameters. Having too many type parameters or using constraints where they aren’t needed can make inference less successful, frustrating callers of your function.
#### Push Type Parameters Down
Here are two ways of writing a function that appear similar:
```
tsfunction firstElement1<Type>(arr: Type[]) {return arr[0];}function firstElement2<Type extends any[]>(arr: Type) {return arr[0];}// a: number (good)const a = firstElement1([1, 2, 3]);// b: any (bad)const b = firstElement2([1, 2, 3]);Try
```
These might seem identical at first glance, but `firstElement1` is a much better way to write this function. Its inferred return type is `Type`, but `firstElement2`’s inferred return type is `any` because TypeScript has to resolve the `arr[0]` expression using the constraint type, rather than “waiting” to resolve the element during a call.
> **Rule**: When possible, use the type parameter itself rather than constraining it
#### Use Fewer Type Parameters
Here’s another pair of similar functions:
```
tsfunction filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {return arr.filter(func);}function filter2<Type, Func extends (arg: Type) => boolean>(arr: Type[],func: Func): Type[] {return arr.filter(func);}Try
```
We’ve created a type parameter `Func` that *doesn’t relate two values*. That’s always a red flag, because it means callers wanting to specify type arguments have to manually specify an extra type argument for no reason. `Func` doesn’t do anything but make the function harder to read and reason about\!
> **Rule**: Always use as few type parameters as possible
#### Type Parameters Should Appear Twice
Sometimes we forget that a function might not need to be generic:
```
tsfunction greet<Str extends string>(s: Str) {console.log("Hello, " + s);}greet("world");Try
```
We could just as easily have written a simpler version:
```
tsfunction greet(s: string) {console.log("Hello, " + s);}Try
```
Remember, type parameters are for *relating the types of multiple values*. If a type parameter is only used once in the function signature, it’s not relating anything. This includes the inferred return type; for example, if `Str` was part of the inferred return type of `greet`, it would be relating the argument and return types, so would be used *twice* despite appearing only once in the written code.
> **Rule**: If a type parameter only appears in one location, strongly reconsider if you actually need it
## Optional Parameters
Functions in JavaScript often take a variable number of arguments. For example, the `toFixed` method of `number` takes an optional digit count:
```
tsfunction f(n: number) {console.log(n.toFixed()); // 0 argumentsconsole.log(n.toFixed(3)); // 1 argument}Try
```
We can model this in TypeScript by marking the parameter as *optional* with `?`:
```
tsfunction f(x?: number) {// ...}f(); // OKf(10); // OKTry
```
Although the parameter is specified as type `number`, the `x` parameter will actually have the type `number | undefined` because unspecified parameters in JavaScript get the value `undefined`.
You can also provide a parameter *default*:
```
tsfunction f(x = 10) {// ...}Try
```
Now in the body of `f`, `x` will have type `number` because any `undefined` argument will be replaced with `10`. Note that when a parameter is optional, callers can always pass `undefined`, as this simply simulates a “missing” argument:
```
ts// All OKf();f(10);f(undefined);Try
```
### Optional Parameters in Callbacks
Once you’ve learned about optional parameters and function type expressions, it’s very easy to make the following mistakes when writing functions that invoke callbacks:
```
tsfunction myForEach(arr: any[], callback: (arg: any, index?: number) => void) {for (let i = 0; i < arr.length; i++) {callback(arr[i], i);}}Try
```
What people usually intend when writing `index?` as an optional parameter is that they want both of these calls to be legal:
```
tsmyForEach([1, 2, 3], (a) => console.log(a));myForEach([1, 2, 3], (a, i) => console.log(a, i));Try
```
What this *actually* means is that *`callback` might get invoked with one argument*. In other words, the function definition says that the implementation might look like this:
```
tsfunction myForEach(arr: any[], callback: (arg: any, index?: number) => void) {for (let i = 0; i < arr.length; i++) {// I don't feel like providing the index todaycallback(arr[i]);}}Try
```
In turn, TypeScript will enforce this meaning and issue errors that aren’t really possible:
```
tsmyForEach([1, 2, 3], (a, i) => {console.log(i.toFixed());'i' is possibly 'undefined'.18048'i' is possibly 'undefined'.});Try
```
In JavaScript, if you call a function with more arguments than there are parameters, the extra arguments are simply ignored. TypeScript behaves the same way. Functions with fewer parameters (of the same types) can always take the place of functions with more parameters.
> **Rule**: When writing a function type for a callback, *never* write an optional parameter unless you intend to *call* the function without passing that argument
## Function Overloads
Some JavaScript functions can be called in a variety of argument counts and types. For example, you might write a function to produce a `Date` that takes either a timestamp (one argument) or a month/day/year specification (three arguments).
In TypeScript, we can specify a function that can be called in different ways by writing *overload signatures*. To do this, write some number of function signatures (usually two or more), followed by the body of the function:
```
tsfunction makeDate(timestamp: number): Date;function makeDate(m: number, d: number, y: number): Date;function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {if (d !== undefined && y !== undefined) {return new Date(y, mOrTimestamp, d);} else {return new Date(mOrTimestamp);}}const d1 = makeDate(12345678);const d2 = makeDate(5, 5, 5);const d3 = makeDate(1, 3);No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.2575No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.Try
```
In this example, we wrote two overloads: one accepting one argument, and another accepting three arguments. These first two signatures are called the *overload signatures*.
Then, we wrote a function implementation with a compatible signature. Functions have an *implementation* signature, but this signature can’t be called directly. Even though we wrote a function with two optional parameters after the required one, it can’t be called with two parameters\!
### Overload Signatures and the Implementation Signature
This is a common source of confusion. Often people will write code like this and not understand why there is an error:
```
tsfunction fn(x: string): void;function fn() {// ...}// Expected to be able to call with zero argumentsfn();Expected 1 arguments, but got 0.2554Expected 1 arguments, but got 0.Try
```
Again, the signature used to write the function body can’t be “seen” from the outside.
> The signature of the *implementation* is not visible from the outside. When writing an overloaded function, you should always have *two* or more signatures above the implementation of the function.
The implementation signature must also be *compatible* with the overload signatures. For example, these functions have errors because the implementation signature doesn’t match the overloads in a correct way:
```
tsfunction fn(x: boolean): void;// Argument type isn't rightfunction fn(x: string): void;This overload signature is not compatible with its implementation signature.2394This overload signature is not compatible with its implementation signature.function fn(x: boolean) {}Try
```
```
tsfunction fn(x: string): string;// Return type isn't rightfunction fn(x: number): boolean;This overload signature is not compatible with its implementation signature.2394This overload signature is not compatible with its implementation signature.function fn(x: string | number) {return "oops";}Try
```
### Writing Good Overloads
Like generics, there are a few guidelines you should follow when using function overloads. Following these principles will make your function easier to call, easier to understand, and easier to implement.
Let’s consider a function that returns the length of a string or an array:
```
tsfunction len(s: string): number;function len(arr: any[]): number;function len(x: any) {return x.length;}Try
```
This function is fine; we can invoke it with strings or arrays. However, we can’t invoke it with a value that might be a string *or* an array, because TypeScript can only resolve a function call to a single overload:
```
tsTry
```
Because both overloads have the same argument count and same return type, we can instead write a non-overloaded version of the function:
```
tsfunction len(x: any[] | string) {return x.length;}Try
```
This is much better! Callers can invoke this with either sort of value, and as an added bonus, we don’t have to figure out a correct implementation signature.
> Always prefer parameters with union types instead of overloads when possible
## Declaring `this` in a Function
TypeScript will infer what the `this` should be in a function via code flow analysis, for example in the following:
```
tsconst user = {id : 123,admin : false,becomeAdmin: function () {this.admin = true;},};Try
```
TypeScript understands that the function `user.becomeAdmin` has a corresponding `this` which is the outer object `user`. `this`, *heh*, can be enough for a lot of cases, but there are a lot of cases where you need more control over what object `this` represents. The JavaScript specification states that you cannot have a parameter called `this`, and so TypeScript uses that syntax space to let you declare the type for `this` in the function body.
```
tsinterface DB {filterUsers(filter: (this: User) => boolean): User[];}const db = getDB();const admins = db.filterUsers(function (this: User) {return this.admin;});Try
```
This pattern is common with callback-style APIs, where another object typically controls when your function is called. Note that you need to use `function` and not arrow functions to get this behavior:
```
tsTry
```
## Other Types to Know About
There are some additional types you’ll want to recognize that appear often when working with function types. Like all types, you can use them everywhere, but these are especially relevant in the context of functions.
### `void`
`void` represents the return value of functions which don’t return a value. It’s the inferred type any time a function doesn’t have any `return` statements, or doesn’t return any explicit value from those return statements:
```
ts// The inferred return type is voidfunction noop() {return;}Try
```
In JavaScript, a function that doesn’t return any value will implicitly return the value `undefined`. However, `void` and `undefined` are not the same thing in TypeScript. There are further details at the end of this chapter.
> `void` is not the same as `undefined`.
### `object`
The special type `object` refers to any value that isn’t a primitive (`string`, `number`, `bigint`, `boolean`, `symbol`, `null`, or `undefined`). This is different from the *empty object type* `{ }`, and also different from the global type `Object`. It’s very likely you will never use `Object`.
> `object` is not `Object`. **Always** use `object`\!
Note that in JavaScript, function values are objects: They have properties, have `Object.prototype` in their prototype chain, are `instanceof Object`, you can call `Object.keys` on them, and so on. For this reason, function types are considered to be `object`s in TypeScript.
### `unknown`
The `unknown` type represents *any* value. This is similar to the `any` type, but is safer because it’s not legal to do anything with an `unknown` value:
```
tsfunction f1(a: any) {a.b(); // OK}function f2(a: unknown) {a.b();'a' is of type 'unknown'.18046'a' is of type 'unknown'.}Try
```
This is useful when describing function types because you can describe functions that accept any value without having `any` values in your function body.
Conversely, you can describe a function that returns a value of unknown type:
```
tsfunction safeParse(s: string): unknown {return JSON.parse(s);}// Need to be careful with 'obj'!const obj = safeParse(someRandomString);Try
```
### `never`
Some functions *never* return a value:
```
tsfunction fail(msg: string): never {throw new Error(msg);}Try
```
The `never` type represents values which are *never* observed. In a return type, this means that the function throws an exception or terminates execution of the program.
`never` also appears when TypeScript determines there’s nothing left in a union.
```
tsfunction fn(x: string | number) {if (typeof x === "string") {// do something} else if (typeof x === "number") {// do something else} else {x; // has type 'never'!}}Try
```
### `Function`
The global type `Function` describes properties like `bind`, `call`, `apply`, and others present on all function values in JavaScript. It also has the special property that values of type `Function` can always be called; these calls return `any`:
```
tsfunction doSomething(f: Function) {return f(1, 2, 3);}Try
```
This is an *untyped function call* and is generally best avoided because of the unsafe `any` return type.
If you need to accept an arbitrary function but don’t intend to call it, the type `() => void` is generally safer.
## Rest Parameters and Arguments
> Background Reading:
> [Rest Parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters)
> [Spread Syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
### Rest Parameters
In addition to using optional parameters or overloads to make functions that can accept a variety of fixed argument counts, we can also define functions that take an *unbounded* number of arguments using *rest parameters*.
A rest parameter appears after all other parameters, and uses the `...` syntax:
```
tsfunction multiply(n: number, ...m: number[]) {return m.map((x) => n * x);}// 'a' gets value [10, 20, 30, 40]const a = multiply(10, 1, 2, 3, 4);Try
```
In TypeScript, the type annotation on these parameters is implicitly `any[]` instead of `any`, and any type annotation given must be of the form `Array<T>` or `T[]`, or a tuple type (which we’ll learn about later).
### Rest Arguments
Conversely, we can *provide* a variable number of arguments from an iterable object (for example, an array) using the spread syntax. For example, the `push` method of arrays takes any number of arguments:
```
tsconst arr1 = [1, 2, 3];const arr2 = [4, 5, 6];arr1.push(...arr2);Try
```
Note that in general, TypeScript does not assume that arrays are immutable. This can lead to some surprising behavior:
```
ts// Inferred type is number[] -- "an array with zero or more numbers",// not specifically two numbersconst args = [8, 5];const angle = Math.atan2(...args);A spread argument must either have a tuple type or be passed to a rest parameter.2556A spread argument must either have a tuple type or be passed to a rest parameter.Try
```
The best fix for this situation depends a bit on your code, but in general a `const` context is the most straightforward solution:
```
ts// Inferred as 2-length tupleconst args = [8, 5] as const;// OKconst angle = Math.atan2(...args);Try
```
Using rest arguments may require turning on [`downlevelIteration`](https://www.typescriptlang.org/tsconfig#downlevelIteration) when targeting older runtimes.
## Parameter Destructuring
> Background Reading:
> [Destructuring Assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
You can use parameter destructuring to conveniently unpack objects provided as an argument into one or more local variables in the function body. In JavaScript, it looks like this:
```
jsfunction sum({ a, b, c }) {console.log(a + b + c);}sum({ a: 10, b: 3, c: 9 });
```
The type annotation for the object goes after the destructuring syntax:
```
tsfunction sum({ a, b, c }: { a: number; b: number; c: number }) {console.log(a + b + c);}Try
```
This can look a bit verbose, but you can use a named type here as well:
```
ts// Same as prior exampletype ABC = { a: number; b: number; c: number };function sum({ a, b, c }: ABC) {console.log(a + b + c);}Try
```
## Assignability of Functions
### Return type `void`
The `void` return type for functions can produce some unusual, but expected behavior.
Contextual typing with a return type of `void` does **not** force functions to **not** return something. Another way to say this is a contextual function type with a `void` return type (`type voidFunc = () => void`), when implemented, can return *any* other value, but it will be ignored.
Thus, the following implementations of the type `() => void` are valid:
```
tstype voidFunc = () => void;const f1: voidFunc = () => {return true;};const f2: voidFunc = () => true;const f3: voidFunc = function () {return true;};Try
```
And when the return value of one of these functions is assigned to another variable, it will retain the type of `void`:
```
tsconst v1 = f1();const v2 = f2();const v3 = f3();Try
```
This behavior exists so that the following code is valid even though `Array.prototype.push` returns a number and the `Array.prototype.forEach` method expects a function with a return type of `void`.
```
tsconst src = [1, 2, 3];const dst = [0];src.forEach((el) => dst.push(el));Try
```
There is one other special case to be aware of, when a literal function definition has a `void` return type, that function must **not** return anything.
```
tsfunction f2(): void {// @ts-expect-errorreturn true;}const f3 = function (): void {// @ts-expect-errorreturn true;};Try
```
For more on `void` please refer to these other documentation entries:
- [FAQ - “Why are functions returning non-void assignable to function returning void?”](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void) |
| Shard | 42 (laksa) |
| Root Hash | 17921559718398937842 |
| Unparsed URL | org,typescriptlang!www,/docs/handbook/2/functions.html s443 |