Last updated: Feb 27, 2024
Reading time·9 min
To declare a function with an object return type, set the return type of the function to an object right after the function's parameter list.
If the return type of the function is not set, TypeScript will infer it.
// ✅ named function function getObj(): { name: string; age: number } { return { name: 'Bobby Hadz', age: 30 }; } // ✅ arrow function const getObj2 = (): { name: string; age: number } => { return { name: 'Bobby Hadz', age: 30 }; };
Here are two more examples.
// ✅ class method class A { getObj(): { name: string; age: number } { return { name: 'Bobby Hadz', age: 30 }; } } // ✅ Using interface interface Person { name: string; age: number; } function getObj3(): Person { return { name: 'Bobby Hadz', age: 30 }; }
You can set the return type of a function right after its parameter list.
interface Person { name: string; age: number; } function getObj4(name: string, age: number): Person { return { name, age }; }
The examples show how to
set the return type of a function
to an object that contains name
and age
properties.
The advantage of setting the return type of the function explicitly is that the type checker would throw an error if you try to return an object of a different type.
interface Person { name: string; age: number; } function getObj3(): Person { // ⛔️ Error: Object literal may only // specify known properties, and 'country' // does not exist in type 'Person'.ts(2322) return { name: 'Bobby', age: 30, country: 'Chile' }; }
If you don't explicitly set the return type of the function, TypeScript will infer it.
// 👇️ function getObj3(): {name: string; age: number; country: string;} function getObj3() { return { name: 'Bobby Hadz', age: 30, country: 'Chile' }; }
If that's the case, you can use an index signature.
interface Person { name: string; age: number; [key: string]: any; // 👈️ index signature } function getObj3(): Person { return { name: 'Bobby Hadz', age: 30, country: 'Chile', city: 'Santiago', }; }
An index signature is used when we don't know all of the names of a type's properties and the type of its values ahead of time.
string
, it will return a value of any
type.You might also see the index signature {[key: string]: string}
in examples. It
represents a key-value structure that when indexed with a string
returns a
value of type string
.
We still get type safety for the properties that we know about in advance.
name
and age
properties have to exist on the object the function returns and have to be of a specific type.If you try to omit the properties or set them to an incorrect type, you'll get an error.
interface Person { name: string; age: number; [key: string]: any; } function getObj3(): Person { // ⛔️ Error: Type 'number' is not assignable // to type 'string'.ts(2322) return { name: 100, age: 30, country: 'Chile', city: 'Santiago' }; }
We passed a number
for the name
property, so the type checker threw an
error.
It's always best to be as explicit as possible and write the most type-safe code.
To declare a function with an array return type, set the return type of the function to an array right after the function's parameter list.
// ✅ Named function returning an array of strings function getArr(): string[] { return ['bobby', 'hadz', 'com']; } // ✅ Arrow function returning an array of numbers const getArr2 = (): number[] => { return [1, 2, 3]; };
Here are 2 more examples.
// ✅ Class method returning an array of objects class A { getArr(): { id: number; name: string }[] { return [{ id: 1, name: 'Bobby Hadz' }]; } } // ✅ Using type as Person[] for function's return value type Person = { name: string; age: number; }; function getArr3(): Person[] { return [{ name: 'Bobby Hadz', age: 30 }]; }
You can set the return type of a function right after its parameter list.
type Person = { name: string; age: number; }; function getArr3(name: string, age: number): Person[] { return [{ name, age }]; }
The examples above show how to set the return type of a function to an array containing different values.
The advantage of setting the return type of the function explicitly is that the type checker would throw an error if you try to return an array of a different type.
function getArr(): string[] { // ⛔️ Error: Type 'number' is not assignable to type 'string'.ts(2322) return [1, 2, 3]; }
If you don't explicitly set the return type of the function, TypeScript will infer it.
// 👇️ function getArr(): string[] function getArr() { return ['bobby', 'hadz', 'com']; }
If your function returns an array of mixed types, e.g. strings and numbers, you have to use a union to specify the function's return type.
function getArr(): (string | number)[] { return ['bobby', 1, 'hadz', 2]; }
The function above returns an array that contains strings and numbers. Notice
that we used parentheses to wrap the values - (string | number)[]
and not
string | number[]
.
If your function returns an
array of objects, create a
type or an
interface and set the function's
return type as Type[]
.
type Person = { name: string; age: number; }; function getArr3(name: string, age: number): Person[] { return [{ name, age }]; }
The function returns an array of objects that contain the name
and age
properties.
If that's the case, you can use an index signature.
type Person = { name: string; age: number; [key: string]: any; // 👈️ index signature }; function getArr3(name: string, age: number): Person[] { return [{ name, age, city: 'Santiago' }]; }
An index signature is used when we don't know all of the names of a type's properties and the type of its values ahead of time.
string
, it will return a value of any
type.You might also see the index signature {[key: string]: string}
in examples. It
represents a key-value structure that when indexed with a string
returns a
value of type string
.
We still get type safety for the properties that we know about in advance.
name
and age
properties have to be set and be of a specific type.If you try to omit the properties or set them to an incorrect type, you'll get an error.
type Person = { name: string; age: number; [key: string]: any; // 👈️ index signature }; function getArr3(): Person[] { // ⛔️ Error: Type 'boolean' is not // assignable to type 'string'.ts(2322) return [{ name: true, age: 30, city: 'Santiago' }]; }
We passed a boolean
for the name
property, so the type checker threw an
error.
It's always best to be as explicit as possible and write the most type-safe code.
To declare a function with a tuple return type, set the return type of the function to a tuple right after the function's parameter list.
If the return type of the function is not set, TypeScript will infer it as
type[]
.
// ✅ Named function returning a tuple function getTuple(): [number, number] { return [5, 10]; } // ✅ Arrow function returning a tuple const getTuple2 = (): [string, string] => { return ['bobby', 'hadz']; }; // ✅ Using a type type Numbers = [number, number]; function getTuple4(): Numbers { return [10, 10]; }
You can set the return type of a function right after its parameter list.
To set the return type of a function to a tuple, pass the types of the tuple between square brackets as if passing elements to an array.
function getTuple(a: number, b: number): [number, number] { return [a, b]; }
The examples show how to set the return type of a function to a tuple containing different values.
The advantage of setting the return type of the function explicitly is that the type checker would throw an error if you try to return a tuple of a different type.
function getTuple(): [number, number] { // ⛔️ Error: Type '[number, number, number]' // is not assignable to type '[number, number]' return [10, 100, 1000]; }
If you don't explicitly set the return type of the function, TypeScript will
infer it to be number[]
instead of [number, number]
.
// 👇️ function getTuple(): number[] function getTuple() { return [10, 100]; }
You might also see a const assertion being used in a similar way to how we typed the return value of the function.
// 👇️ function getTuple3(): readonly [10, 10] function getTuple3() { return [10, 10] as const; }
The function in the example returns a readonly
array containing 2
numbers
with a value of 10
.
There are a couple of differences between the two approaches.
If you don't explicitly set the type as readonly
or use a const assertion, it
can still be mutated.
function getTuple(): [number, number] { return [10, 100]; } const t = getTuple(); t.push(1000); console.log(t); // 👉️ [10, 100, 1000]
If you want to use this approach but want the tuple to be read-only, pass the
return type of the function to the Readonly
utility type.
function getTuple(): Readonly<[number, number]> { return [10, 100]; } const t = getTuple(); // ⛔️ Error: Property 'push' does not // exist on type 'readonly [number, number]'.ts(2339) t.push(1000);
The
Readonly
utility type constructs a type with all properties set to readonly
.
Use the Readonly
utility type to declare a function with a read-only return
type.
The Readonly
utility type constructs a type with all properties set to
readonly
. The properties of the constructed type cannot be reassigned.
function getArr(): Readonly<string[]> { return ['bobby', 'hadz', 'com']; } function getObj(): Readonly<{ name: string }> { return { name: 'Bobby Hadz' }; }
Here are 2 more examples.
function getSet(): ReadonlySet<string> { return new Set(['bobby', 'hadz', 'com']); } function getMap(): ReadonlyMap<string, string> { return new Map([['name', 'Bobby Hadz']]); }
We used the Readonly utility type to type a function whose return value only permits reading.
You can set the return type of a function right after its parameter list.
The 2 functions in the following code snippet have the same return type - a
readonly
array of strings.
function getArr(): Readonly<string[]> { return ['bobby', 'hadz', 'com']; } function getArr2(): ReadonlyArray<string> { return ['a', 'b', 'c']; }
If you try to change a value in the readonly
array, you'd get an error.
function getArr(): Readonly<string[]> { return ['bobby', 'hadz', 'com']; } const a = getArr(); // ⛔️ Index signature in type 'readonly string[]' // only permits reading.ts(2542) a[0] = 'z';
The same is the case when working with read-only objects.
function getObj(): Readonly<{ name: string }> { return { name: 'Bobby Hadz' }; } const obj = getObj(); // ⛔️ Error: Property 'age' does not exist // on type 'Readonly<{ name: string; }>'.ts(2339) obj.age = 30;
When working with Set or Map objects, make sure to use the corresponding utility
types - ReadonlySet
and ReadonlyMap
.
function getSet(): ReadonlySet<string> { return new Set(['bobby', 'hadz', 'com']); } function getMap(): ReadonlyMap<string, string> { return new Map([['name', 'Bobby Hadz']]); }
The first type we passed to the ReadonlyMap
utility type is the type for the
keys, and the second - for the values.
If you need to pass a function as a parameter, check out the following article.
You can learn more about the related topics by checking out the following tutorials: