Modules page
Learn how to export and import values from a file.
Overview
In this section, you will:
- Export values from a file.
- Import values from another file.
- Learn why named exports are preferred over default exports.
- Import and export functions between modules.
Objective 1: Export and import values
A fundamental part of writing code is creating values that you can use across multiple files. Modules are a way of grouping code to be executed within their own scope, opposed to a global scope. Modules give us a way to write DRY code, scope our variables, re-use parts of our code, test our code in discrete units, and avoid writing applications full of dreadful spaghetti code. Let’s break down how to export and import values in TypeScript.
In TypeScript, you can export any type of value: variables, functions, classes, interfaces, etc.
There are two types of exports: default exports and named exports.
Default exports
Imagine we have a config.ts
file:
const config = {
name: "Bitovi Academy",
url: "https://www.bitovi.com/academy/",
};
export default config;
In the example above, we create a config
variable and then use export default
to export it.
Then, we could consume the value in another file:
import appConfig from "./config.ts";
const appName = appConfig.name;
In the code above, the appConfig
variable is the same as the config
variable exported from config.ts
.
Note that the appConfig
variable name does not need to match the name in the file that has the export
.
Named exports
Named exports are called that because there is an explicit name in the file with the export
.
Let’s imagine we have a helpers.ts
file:
export function constrain(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}
In the example above, we export a constrain
function from our helpers.ts
file.
We can import the constrain
function like so:
import { constrain } from "./helpers.ts";
const constrained = constrain(-18, 0, 100);
In the code above, we can import the constrain
function by name from helpers.ts
and use it in our code.
Default exports vs. named exports
While you can prefer one style of export over the other, each serves a different purpose.
- Use default exports for the primary output of a module.
- Use named exports for secondary outputs or when there is no primary output.
For example:
- If you have a module called
AuthProvider
that exports a React component and several helper hooks, the component would be the primary (default) export and the hook would be secondary (named). - If you have a module called
transforms
that exports the functionsformatPercent
andformatCurrency
, both of these would be named exports; there is no primary export.
Importing types
From time to time, you may need to only import the type
from another file and not the full implementation.
TypeScript provides a special syntax, import type
, which is used specifically to import type declarations.
This can help to make the import intention clearer and can also have some performance benefits during compilation.
Let’s imagine we have a user.ts
file:
export type User = {
age: number;
name: string;
};
export const createUser = (name: string, age: number): User => {
return { name, age };
};
In the code above, we defined a User
type and a createUser
function.
The User
type describes the shape of user objects.
We can import just the User
type (and use it) like so:
import type { User } from "./user";
import { createUser } from "./user";
const logUser = (user: User): void => {
console.info(`User: ${user.name}, Age: ${user.age}`);
};
const user = createUser("Alice", 30);
logUser(user);
In the code above, the User
type import will not be included in the JavaScript output after compilation (because it’s only used for static type checking by TypeScript).
Use import type
whenever you are importing types like interfaces, types, or type aliases from another module and you do not need to import any runtime variables, classes, or functions.
This not only makes your imports cleaner and more specific, but it also optimizes your project’s output size and compilation time.
Setup
✏️ Create src/modules/exporter.ts and update it to be:
function greeter(person: string) {
return "Hello, " + person;
}
// Exercise: export greeter
✏️ Create src/modules/importer.ts and update it to be:
// Exercise: import greeter
export default function () {
return greeter("Earth");
}
Verify
✏️ Create src/modules/importer.test.ts and update it to be:
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import getMessageWithGreeting from "./importer";
describe('Importer', () => {
it('should return a greeting', () => {
const message = getMessageWithGreeting();
assert.strictEqual(message, "Hello, Earth", "message should be correct");
});
});
Exercise
Use your knowledge of named exports and imports to do the following:
- Export
greeter
fromexporter.ts
- Import
greeter
inimporter.ts
Having issues with your local setup? You can use either StackBlitz or CodeSandbox to do this exercise in an online code editor.
Solution
If you’ve implemented the solution correctly, the tests will pass when you run npm run test
!
Click to see the solution
✏️ Update src/modules/exporter.ts to be:
export function greeter(person: string) {
return "Hello, " + person;
}
✏️ Update src/modules/importer.ts to be:
import { greeter } from "./exporter"
export default function () {
return greeter("Earth");
}
Having issues with your local setup? See the solution in StackBlitz or CodeSandbox.
Bonus exploration
What does your IDE show when you try to:
- Hover over
greeter
? - Pass a number to
greeter
?
Next steps
Next, let’s learn how to declare types in TypeScript.