Expo: TypeScript & Path Aliases
📱

Expo: TypeScript & Path Aliases

Initialize Project [commit]

% expo init expo-amplify-starter && cd $_ ✔ Choose a template: › blank (TypeScript)

Restructure Project [commit]

This part is up to you but I prefer to move my code to src/ and structure my projects the following way,
.expo-shared node_modules src ├── api ├── assets ├── components ├── screens ├── i18n ├── navigation ├── screens ├── services ├── styles ├── utilities ├── App.tsx .gitignore index.js <-- We are about to create this file app.json babel.config.js package.json tsconfig.json yarn.lock

Fix App Entry Point

Since we moved App.tsx from the root directory, which is where Expo expects our App to be, we have to create a new entry point file in the root and point to it.
// index.js import { registerRootComponent } from 'expo'; import App from './src/App'; export default registerRootComponent(App);
I'm pretty sure this file needs to be named index.js and be in the root of the directory. I had it different before and although it worked locally, my EAS builds failed.
// package.json { "main": "./index.js", ... }

Fix Asset Paths

Since we moved the assets, we have to also update those paths too.
// app.json { "expo": { ... "icon": "./src/assets/icon.png", "splash": { "image": "./src/assets/splash.png", ... }, ... "android": { "adaptiveIcon": { "foregroundImage": "./src/assets/adaptive-icon.png", ... } }, "web": { "favicon": "./src/assets/favicon.png" } } }

Create Path Aliases [commit]

Path aliases (also called module resolution depending on the tool) are a nice way to import parts of your project without having to specify an exact path. This not only saves you typing but makes it easier to restructure later if need be. Since Expo utilizes different build tools, you have to declare these in multiple places.
import { SomeUtility } from '../../utilities/MyUtility'; // Changes to import { SomeUtility } from '@utilities/MyUtility';

Create Path Aliases for TypeScript

// tsconfig.json { ... "compilerOptions": { ... "baseUrl": "./src", "paths": { "@api/*": ["api/*"], "@assets/*": ["assets/*"], "@components/*": ["components/*"], "@i18n/*": ["i18n/*"], "@navigation/*": ["navigation/*"], "@screens/*": ["screens/*"], "@services/*": ["services/*"], "@styles/*": ["styles/*"], "@utilities/*": ["utiltiies/*"], } }, }

Create Path Aliases for Expo Web

First we have to get access to the webpack configuration. Run expo customize:web and select to generate webpack.config.js
// webpack.config.js const createExpoWebpackConfigAsync = require('@expo/webpack-config'); const path = require('path'); module.exports = async (env, argv) => { const config = await createExpoWebpackConfigAsync(env, argv); config.resolve.alias = { ...config.resolve.alias, '@api': path.resolve(__dirname, 'src/api/'), '@assets': path.resolve(__dirname, 'src/assets/'), '@components': path.resolve(__dirname, 'src/components/'), '@i18n': path.resolve(__dirname, 'src/i18n/'), '@navigation': path.resolve(__dirname, 'src/navigation/'), '@screens': path.resolve(__dirname, 'src/screens/'), '@services': path.resolve(__dirname, 'src/services/'), '@styles': path.resolve(__dirname, 'src/styles/'), '@utilities': path.resolve(__dirname, 'src/utilities/'), }; return config; };

Path Aliases for iOS/android

yarn add -D babel-plugin-module-resolver
// .babelrc.js { presets: [ ... ], plugins: [ ... [ 'module-resolver', { root: ['./'], alias: { '@api': './src/api', '@assets': './src/assets', '@components': './src/components', '@i18n': './src/i18n', '@navigation': './src/navigation', '@screens': './src/screens', '@services': './src/services', '@styles': './src/styles', '@utilities': './src/utilities', }, } ] ] }

Path Aliases for Jest

If you are using Jest you will also have to configure the path aliases for it as well.
// jest.config.js module.exports = { ... moduleNameMapper: { '^@api/(.*)': '<rootDir>/src/api/$1', '^@assets/(.*)': '<rootDir>/src/assets/$1', '^@components/(.*)': '<rootDir>/src/components/$1', '^@i18n/(.*)': '<rootDir>/src/i18n/$1', '^@navigation/(.*)': '<rootDir>/src/navigation/$1', '^@screens/(.*)': '<rootDir>/src/screens/$1', '^@services/(.*)': '<rootDir>/src/services/$1', '^@styles/(.*)': '<rootDir>/src/styles/$1', '^@utilities/(.*)': '<rootDir>/src/utilities/$1', }, };

Test Path Aliases

Okay, now that we have added the aliases to all the files we needed to, let's test it to make sure it is working on iOS, Android, and the web.
// src/utilities/MyUtility export const SomeUtility = () => { return 'Success' };
// src/App.tsx ... import { SomeUtility } from '@utilities/MyUtility'; export default function App() { return ( <View style={styles.container}> <Text>Path Alias: {SomeUtility()}</Text> <StatusBar style="auto" /> </View>); } ...
Test your app with yarn start -c (-c clears the cache). You should see "Path Alias: Success" on all the platforms.
Click to rocket boost to the top of the page!