WebAssembly y TypeScript: técnicas avanzadas de integración

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción a la integración avanzada de WebAssembly y TypeScript



WebAssembly (Wasm) revolucionó el desarrollo web, permitiendo ejecutar código a una velocidad cercana a la nativa directamente en el navegador. Sin embargo, a nivel avanzado, los desarrolladores se enfrentan al desafío: ¿cómo combinar eficazmente el tipado estricto de TypeScript con las capacidades de bajo nivel de Wasm? En este artículo, exploraremos técnicas que van más allá de los ejemplos básicos: gestión manual de memoria, creación de envoltorios con tipado completo, optimización de llamadas y compilación de proyectos complejos.



El material está dirigido a desarrolladores que ya conocen los fundamentos de Wasm y TypeScript. No nos detendremos en la compilación de "Hello World": nos centraremos en tareas reales: trabajo con búferes, estructuras de datos, multiprocesamiento y perfilado.



1. Gestión avanzada de memoria: asignación manual y estructuras



El ejemplo básico de exportar una función de Wasm a TypeScript normalmente trabaja con primitivas. A nivel avanzado, necesitarás transferir datos complejos: cadenas, arreglos de estructuras, imágenes. Veamos cómo se hace esto a través de WebAssembly.Memory.



1.1. Asignación y liberación de memoria



Supongamos que tenemos un módulo Wasm, escrito en AssemblyScript o C, que exporta las funciones malloc y free. En TypeScript creamos un envoltorio:



// types.tsexport interface WasmMemory {  malloc(size: number): number;  free(ptr: number): void;  memory: WebAssembly.Memory;}

// memory-manager.tsexport class MemoryManager { private wasm: WasmMemory;

constructor(wasm: WasmMemory) { this.wasm = wasm; }

allocateString(str: string): number { const encoder = new TextEncoder(); const bytes = encoder.encode(str); const ptr = this.wasm.malloc(bytes.length + 1); // +1 para el terminador nulo const view = new Uint8Array(this.wasm.memory.buffer); view.set(bytes, ptr); view[ptr + bytes.length] = 0; // terminador nulo return ptr; }

readString(ptr: number): string { const view = new Uint8Array(this.wasm.memory.buffer); let end = ptr; while (view[end] !== 0) end++; const bytes = view.slice(ptr, end); return new TextDecoder().decode(bytes); }

freeString(ptr: number): void { this.wasm.free(ptr); }}


Este enfoque garantiza que la memoria no se filtre y que TypeScript controle completamente el ciclo de vida de los datos.



1.2. Trabajo con estructuras de datos



Para transferir objetos (por ejemplo, un punto con coordenadas {x: f32, y: f32}) usa DataView:



interface Point { x: number; y: number; }

export class PointManager { private wasm: WasmMemory; private readonly SIZE = 8; // 2 float32

constructor(wasm: WasmMemory) { this.wasm = wasm; }

createPoint(p: Point): number { const ptr = this.wasm.malloc(this.SIZE); const view = new DataView(this.wasm.memory.buffer); view.setFloat32(ptr, p.x, true); view.setFloat32(ptr + 4, p.y, true); return ptr; }

readPoint(ptr: number): Point { const view = new DataView(this.wasm.memory.buffer); return { x: view.getFloat32(ptr, true), y: view.getFloat32(ptr + 4, true) }; }}


Importante: siempre verifica la alineación de la memoria. Wasm requiere alineación de 4 bytes para f32.



2. Tipado de importaciones y exportaciones de Wasm en TypeScript



Sin tipado, trabajar con Wasm es peligroso: puedes pasar un número en lugar de un puntero y obtener un segfault. Usa interfaces estrictas.



2.1. Generación de tipos a partir de Wasm



La herramienta wasm-objdump o wasm2wat permite obtener las firmas de las funciones. Para un proyecto en AssemblyScript, usa el generador de tipos:



// wasm-types.d.tsdeclare namespace WasmModule {  interface Exports {    add(a: number, b: number): numb                

Blogs

Book Recommendations