---
title: TypeScript functions
description: Learn how to create and use EAS Build functions in your custom build configurations.
---
EAS Build functions are a great way to extend the functionality of custom builds. You can use them to create reusable steps, and to write your logic in JavaScript, TypeScript, or Bash (more information in [`command` in the config schema](./schema.md#functionsfunction_namecommand)). This guide provides a walkthrough of creating a function in TypeScript.
## Initialize an EAS Build function module
The easiest way to create an EAS Build function is to use the `create-eas-build-function` CLI tool. By running the following command from the same directory as your **eas.json** file, you can create a new custom TypeScript function:
This creates a new module called `myFunction` in the **.eas/build** directory. The module will contain a pre-generated module configuration and the **src** directory with the **index.ts** file containing the default TypeScript function template.
```ts .eas/build/myFunction/src/index.ts
// This file was autogenerated by `create-eas-build-function` command.
// Go to README.md to learn more about how to write your own custom build functions.
// interface FunctionInputs {
// // specify the type of the inputs value and whether they are required here
// // example: name: BuildStepInput<BuildStepInputValueTypeName.STRING, true>;
// }
// interface FunctionOutputs {
// // specify the function outputs and whether they are required here
// // example: name: BuildStepOutput<true>;
// }
async function myFunction(
ctx: BuildStepContext
// {
// inputs,
// outputs,
// env,
// }: {
// inputs: FunctionInputs;
// outputs: FunctionOutputs;
// env: BuildStepEnv;
// }
): Promise<void> {
ctx.logger.info('Hello from my TypeScript function!');
}
```
## Compile the function
Functions must be compiled to a single JavaScript file that can be run without installing any dependencies. The default `build` script for generated functions uses [ncc](https://github.com/vercel/ncc) to compile your function into a single file with all its dependencies. If you don't have the `ncc` installed globally on your machine, run `npm install -g @vercel/ncc` to install it. Next, run the build script in the **.eas/build/myFunction** directory:
This command triggers the `build` script placed in the **package.json** file of your custom function module.
```json package.json
{
...
"scripts": {
...
"build": "ncc build ./src/index.ts -o build/ --minify --no-cache --no-source-map-register" /* @end */
...
},
...
}
```
The `build` script generates **build/index.js**. This file must be uploaded to EAS Build as a part of your project archive, so that the builder can run your function. Ensure that the file is not excluded by a **.gitignore** file or **.easignore** file.
## Expose the function to the custom build config
> **Note**: The following example assumes that you have already set up a custom build workflow and configured it in your **eas.json**. If not, see [Get started with custom builds](/custom-builds/get-started/#create-a-workflow) before proceeding.
Let's assume that you have a **config.yml** file in the **.eas/build** directory. It contains the following content:
```yaml .eas/build/config.yml
build:
name: My example config
steps:
- eas/checkout
- eas/install_node_modules
- run:
name: Finished
command: echo Finished
```
To add your function to the config, you need to add the following lines to the **config.yml** file:
```yaml .eas/build/config.yml
build:
name: My example config
steps:
- eas/checkout
- eas/install_node_modules
- run:
name: Finished
command: echo Finished
# @info #
functions:
my_function:
name: My function
path: ./myFunction
# @end #
```
The `path` property should be a relative path from the config file to your function directory. In this case, it's just `./myFunction`.
Now, add a call to the `my_function` function in the **config.yml** file:
```yaml .eas/build/config.yml
build:
name: My example config
steps:
- eas/checkout
- eas/install_node_modules
# @info #
- my_function
# @end #
- run:
name: Finished
command: echo Finished
functions:
my_function:
name: My function
path: ./myFunction
```
## Working on the function
For a more advanced example, let's say you want to make a function calculate the sum of two numbers and print the result to the console, and then output that value from the function. To do this, modify the **config.yml** and **index.ts** files to make the function accept two inputs called `num1` and `num2` and return their sum as an output called `sum`.
```yaml .eas/build/config.yml
build:
name: My example config
steps:
- eas/checkout
- eas/install_node_modules
# @info #
- my_function:
inputs:
num1: 1
num2: 2
id: sum_function
# @end #
- run:
name: Print the sum
inputs:
sum: ${ steps.sum_function.sum }
command: echo ${ inputs.sum }
- run:
name: Finished
command: echo Finished
functions:
my_function:
name: My function
# @info #
inputs:
- name: num1
type: number
- name: num2
type: number
outputs:
- name: sum
# @end #
path: ./myFunction
```
```ts .eas/build/myFunction/src/index.ts
// This file was autogenerated by `create-eas-build-function` command.
// Go to README.md to learn more about how to write your own custom build functions.
import {
BuildStepContext,
BuildStepInput,
BuildStepInputValueTypeName,
BuildStepOutput,
} from '@expo/steps';
/* @info */
interface FunctionInputs {
// first template argument is the type of the input value, second template argument is a boolean indicating if the input is required
num1: BuildStepInput<BuildStepInputValueTypeName.NUMBER, true>;
num2: BuildStepInput<BuildStepInputValueTypeName.NUMBER, true>;
}
/* @end */
/* @info */
interface FunctionOutputs {
// template argument is a boolean indicating if the output is required
sum: BuildStepOutput<true>;
}
/* @end */
async function myFunction(
ctx: BuildStepContext,
{
inputs,
outputs,
}: // env,
{
inputs: FunctionInputs;
outputs: FunctionOutputs;
// env: BuildStepEnv;
}
): Promise<void> {
/* @info */
ctx.logger.info(`num1: ${inputs.num1.value}`);
ctx.logger.info(`num2: ${inputs.num2.value}`);
const sum = inputs.num1.value + inputs.num2.value;
ctx.logger.info(`sum: ${sum}`);
outputs.sum.set(sum.toString()); // Currently, outputs must be strings. This will improve in the future.
/* @end */
}
```
> **Info** Remember to compile your function each time you make changes to it: `npm run build`.
## Summary
- Writing functions is a great way to extend the functionality of custom builds with your own logic.
- EAS Build functions are reusable — you can use them in multiple custom build configurations.
- Using EAS Build functions is a great option for more advanced use cases that are not easy to do by writing shell scripts.
- Most of the [built-in functions](/custom-builds/schema/#built-in-eas-functions) are open-source and can be forked or used as a reference for writing your own functions.
Check out the **example repository** for more detailed examples: