Expo--system-class-api-migration
/SKILLCorrection des erreurs de migration du système de fichiers expo lors de la mise à jour vers le SDK 52+ d'Expo. À utiliser lorsque :
name: expo-file-system-class-api-migration
description: |
Fix for expo-file-system migration errors when upgrading to Expo SDK 52+. Use when:
(1) TypeScript error "Property 'cacheDirectory' does not exist on type",
(2) "Module has no exported member 'EncodingType'" error,
(3) "Property 'deleteAsync' does not exist" or similar FileSystem function errors,
(4) file.info returns a function instead of file info object,
(5) Upgrading Expo SDK and expo-file-system code breaks.
Covers migration from legacy function-based API (FileSystem.deleteAsync, getInfoAsync)
to new class-based API (File, Directory, Paths classes).
author: Claude Code
version: 1.0.0
date: 2025-01-23
Expo File System Class API Migration
Problem
After upgrading to Expo SDK 52+ (expo-file-system 18+), the legacy function-based API
no longer works. Code using FileSystem.deleteAsync(), FileSystem.getInfoAsync(),
FileSystem.cacheDirectory, etc. will fail with TypeScript errors or runtime issues.
Context / Trigger Conditions
You'll encounter this when:
- Upgrading Expo SDK from 51 or earlier to 52+
- TypeScript errors like:
Property 'cacheDirectory' does not exist on type 'typeof import("expo-file-system")'Module '"expo-file-system"' has no exported member 'EncodingType'Property 'deleteAsync' does not existProperty 'getInfoAsync' does not exist- Runtime issues where
file.inforeturns a function instead of file metadata - Any code importing
import * as FileSystem from 'expo-file-system'
Solution
1. Change Import Style
// OLD (legacy API)
import * as FileSystem from 'expo-file-system';
// NEW (class-based API)
import { File, Directory, Paths } from 'expo-file-system';2. Replace Common Operations
| Legacy API | New Class-Based API |
|---|---|
FileSystem.cacheDirectory | Paths.cache |
FileSystem.documentDirectory | Paths.document |
FileSystem.deleteAsync(uri, { idempotent: true }) | new File(uri).delete() |
FileSystem.getInfoAsync(uri) | new File(uri).exists (for existence check) |
FileSystem.writeAsStringAsync(uri, content, { encoding: 'base64' }) | new File(path, name).write(content, { encoding: 'base64' }) |
FileSystem.readAsStringAsync(uri) | new File(uri).text() |
3. File Creation Pattern
// OLD
const filePath = `${FileSystem.cacheDirectory}voice_${Date.now()}.mp3`;
await FileSystem.writeAsStringAsync(filePath, base64Data, {
encoding: FileSystem.EncodingType.Base64
});
// NEW
const fileName = `voice_${Date.now()}.mp3`;
const file = new File(Paths.cache, fileName);
await file.write(base64Data, { encoding: 'base64' });
const uri = file.uri;4. File Deletion Pattern
// OLD
await FileSystem.deleteAsync(uri, { idempotent: true });
// NEW
try {
const file = new File(uri);
if (file.exists) {
await file.delete();
}
} catch {
// Ignore delete errors (equivalent to idempotent)
}5. File Info Pattern
// OLD
const info = await FileSystem.getInfoAsync(uri);
if (info.exists && info.modificationTime) {
// use info.modificationTime
}
// NEW - Note: info is now a METHOD, not a property
const file = new File(uri);
if (file.exists) {
// For modification time, use filename timestamps or other approach
// The new API structure differs for file metadata
}6. Directory Listing
// OLD
const files = await FileSystem.readDirectoryAsync(cacheDir);
// NEW
const cacheDir = Paths.cache;
const contents = cacheDir.list(); // Returns array of File/Directory objects
for (const item of contents) {
if (item instanceof File) {
console.log(item.name, item.uri);
}
}Verification
After migration:
- Run
npx tsc --noEmit- should pass with no expo-file-system errors - Test file operations in the app to ensure they work correctly
Example
Complete before/after for a voice message cache cleanup:
// BEFORE (Legacy API)
import * as FileSystem from 'expo-file-system';
export async function cleanupAudioCache(maxAgeMs: number): Promise<void> {
const cacheDir = FileSystem.cacheDirectory;
if (!cacheDir) return;
const files = await FileSystem.readDirectoryAsync(cacheDir);
const now = Date.now();
for (const fileName of files) {
if (fileName.startsWith('voice_') && fileName.endsWith('.mp3')) {
const filePath = `${cacheDir}${fileName}`;
const info = await FileSystem.getInfoAsync(filePath);
if (info.exists && info.modificationTime) {
const age = now - info.modificationTime * 1000;
if (age > maxAgeMs) {
await FileSystem.deleteAsync(filePath, { idempotent: true });
}
}
}
}
}
// AFTER (Class-Based API)
import { File, Paths } from 'expo-file-system';
export async function cleanupAudioCache(maxAgeMs: number): Promise<void> {
const cacheDir = Paths.cache;
const contents = cacheDir.list();
const now =