Cross-Language Programming in the AI Era: Calling C++ from TypeScript for High-Performance Computing

How to call C++ from TypeScript via Node.js N-API with zero-copy optimization for GB-scale vector computation.
This article walks through a Bilibili demo showing a hybrid architecture that pairs TypeScript business logic with C++ high-performance computation. Using Node.js's N-API native addon mechanism, C++ is compiled into a `.node` dynamic library callable from TS, with CMake and cmake-js handling the build. The article dives into two universal cross-language challenges — environment context and variable lifecycle management — and the zero-copy strategy for GB-scale data: sharing off-heap TypedArray memory directly between JS and C++ to bypass GC and avoid data copies. Tests show interface call overhead is negligible; the real bottleneck is memory initialization, which can be optimized by pre-allocating on the host side. The architecture also lays the groundwork for integrating CUDA GPU computation.
Why Cross-Language Programming Matters in the AI Era
As large models and AI agent development take off, no single programming language can cover every scenario. A content creator on Bilibili made this point clearly: developers in the AI era need broader knowledge, because "you can have AI execute your ideas, but first you need to know what's possible." AI can execute and assist with judgment, but it lacks the ability to make final calls on feasibility — cost analysis and technology selection still require human decision-making.
TypeScript (TS) has long outgrown its frontend roots. From AI agent development and HarmonyOS applications to server-side programs, TS continues to gain ground thanks to its massive ecosystem. C++, on the other hand, remains irreplaceable for high-performance computing. Combining the two — using TS for project organization and business logic while delegating heavy computation to C++ — is a pragmatic architectural approach.
This article is based on that Bilibili demonstration, walking through how to use Node.js's C++ extension mechanism to call C++ from TS for vector computation related to large models, with a particular focus on zero-copy performance optimization.
Technical Architecture: Three Components Working Together
The demonstration involves three distinct pieces of code, each with a clear role:
- CMake code: Handles the build configuration, compiling C++ into a dynamic library that Node.js can load
- C++ code: Implements the underlying computation logic and exposes interfaces via the Node API
- TypeScript code: Acts as the caller, organizing data and triggering C++ computation

The build is done using Visual Studio and CMake. One important detail is handling the dynamic library file extension: on Windows, dynamic libraries default to .dll, but Node.js native modules require a .node extension. This means you need to explicitly specify a SHARED library output in CMake and override the suffix.
Additionally, a Node.js installation typically already includes the necessary header files and libraries (such as those related to cmake-js). You just need to reference them in CMake and pin the version to avoid compilation issues caused by version mismatches. Build artifacts land in the Debug/Release directory by default; in this demo, CMake is configured to automatically copy them to the current path for convenience.
Node.js Native Addons are a mechanism that allows developers to write modules in C/C++ and call them directly from JavaScript or TypeScript. Under the hood, they rely on Node-API (N-API) — an official, stable C interface provided by Node.js, designed to abstract away V8 engine version differences so that compiled .node files don't need to be recompiled when Node.js is upgraded. The community previously used nan (Native Abstractions for Node.js), but N-API has become the dominant choice due to its ABI stability. cmake-js integrates the CMake build system with the Node.js native addon workflow and is more Windows-friendly than the traditional node-gyp, working more naturally with the Visual Studio toolchain. A .node file is essentially a dynamically linked library (DLL on Windows, .so on Linux) — just with a .node extension by Node.js convention — loaded at runtime via require().
C++ Side: Entry Registration and Interface Exposure
A key insight emphasized in the demo is a universal cross-language programming principle: no matter what you're writing, always start by finding the entry point. Just like a standard C++ program has main and an ESP32 program has app_main, Node.js native modules have their own entry point — the init initialization function.
This entry function needs to be registered using a Node API macro. You write an init function that returns napi_value, use it to set up module exports, and then declare it with the registration macro. On the TS side, this corresponds directly to the module loading step (require/load).

There's a common pattern across all cross-language programming: language interop fundamentally involves two things — environment context and variable lifecycle management. Whether C++ is calling Python, Lua, or JS, there's always an environment variable (context/env) and lifecycle interfaces for variables inside the host language. C++ needs both to safely read parameters and create return values.
Specific computation functions (like the vector dot product dot) also return napi_value. Unlike init, computation functions are bound to the export object, similar to member functions of an object. The function retrieves the parameters passed in from TS via callback info, performs the computation, and wraps the result back into a napi_value for return.
Zero-Copy: The Core Challenge of Large-Data Interop
This is the most valuable part of the entire demonstration. The key message: modern data routinely runs into gigabytes, and copying it is expensive. Cross-language interop must minimize data copies as much as possible.
The demo constructs a Float32Array of size 10000 × 10000 × 10 as test data — reaching GB scale. There are several key principles for handling data at this size:

- Bypass garbage collection (GC): Resources this large shouldn't be managed by JavaScript's GC. Memory should be manually freed after computation rather than waiting for passive GC reclamation.
- Share addresses instead of copying: The storage for the result array should have the Node.js-side variable and the C++ side pointing to the same block of memory — no back-and-forth copying. "Whenever you build an interface, you need to think about zero-copy."
- Weights are read and passed in by TS: Model weights are read from file by TS and passed to C++. C++ extracts parameter info and dimension counts from the callback and computes directly on shared memory.

One notable finding from the test: the cost of the interface call itself is negligible. A single computation took about one second, but a significant portion of that time was the initialization of 4 GB of memory space (roughly one second) — not the language interop overhead. This points to a design optimization: pre-initialize the memory space, or have the host (TS side) pre-allocate memory and pass it in, rather than having C++ generate and return a new allocation each time.
The core idea of zero-copy in cross-language scenarios is: have both sides of the language boundary share the same physical memory address, rather than each holding its own copy. In Node.js, the underlying data storage for TypedArray (e.g., Float32Array) and Buffer lives off the V8 heap, which makes it possible to pass a raw memory pointer directly to C++. The data pointer that C++ receives points to the same memory as TypedArray.buffer on the JS side — writes from C++ are immediately visible to JS, and vice versa. N-API provides functions like napi_get_typedarray_info to safely retrieve this memory pointer and length without any data copying. By contrast, if you new a block of memory inside C++ and serialize data back to JS, a copy is unavoidable. For GB-scale data, that copy not only takes time but also doubles peak memory usage — an anti-pattern that must be avoided in high-performance scenarios. The GC risk is this: if C++ holds a raw pointer to JS object memory and the GC moves or collects that object during computation, the result is a dangling pointer. N-API addresses this through reference counting and explicit memory ownership declarations (e.g., napi_create_external_arraybuffer).
Exposing the Interface and Closing the Loop
Having a C++ computation function isn't enough — it must be registered on the export object for TS to access it. The approach: create a function object on the C++ side (napi_create_function), and write it into the export object as a property named dot. Once you've established the relationship between the object, its property, and the property name pointing to the function, TS can call it via cppts.dot(...).
TS code can define full type checking, but in the demo, type constraints are initially skipped to focus on getting the interop chain working. Once the full loop is established, TS handles reading weights, preparing the large array, issuing the call, and timing the result — while C++ performs high-speed computation on shared memory.
Implications for AI Developers
This example matters beyond just being a technical demo. It reflects a shift in the developer skill model for the AI era: the boundaries between technology stacks are blurring, and breadth is increasingly important. Combining TS's ecosystem strengths with C++'s performance advantages through the Node native addon mechanism lets you have both development efficiency and computational performance.
For scenarios like AI agent development and large model inference — where you need both flexible business orchestration and high-performance operators — this "TS + C++ (or even CUDA)" hybrid architecture offers a practical, deployable path. The demo creator also mentioned plans to share more cross-language programming examples, including integrating CUDA GPU computation into this same framework.
One important reminder: AI can help you implement ideas, but validating technical feasibility, pinpointing performance bottlenecks, and weighing cost trade-offs still require developers to get their hands dirty with real experiments.
CUDA (Compute Unified Device Architecture) is NVIDIA's parallel computing platform and programming model, allowing developers to write C-like code (called kernel functions) that runs directly on the GPU. In large model inference, operations like matrix multiplication and vector dot products are natural fits for GPU's massively parallel architecture, achieving throughput improvements of tens to hundreds of times compared to CPU. Integrating CUDA into the "TS + C++" system follows the same approach described in this article: C++ acts as the bridge, calling CUDA kernels inside C++ code to perform GPU computation, then returning results to TypeScript via N-API using shared memory or zero-copy techniques. This path lets developers write agent orchestration logic, scheduling strategies, and business interfaces in TypeScript while seamlessly leveraging GPU compute power underneath — balancing engineering flexibility with inference performance. It's a practical option for locally deploying small models or implementing custom operators.
Related articles

Free Unlimited Claude Code Solution: Agnes AI + CC Switch Setup Guide
No paid subscription needed — pair the free AI platform Agnes AI with the open-source tool CC Switch to provide a backend for Claude Code, enabling unlimited free calls to text, image, and video models. This guide covers the full setup, test results, and usage boundaries.

A Complete Zero-Cost Guide to Getting Started with Claude Code for Beginners in China
A beginner's guide to Claude Code for users in China: two installation methods (command line and IDE plugin), full configuration steps for replacing the official API with the free domestic LongCat model, and a hands-on demo of building a small tool with natural language, plus tips.

When the NSA Gets Involved: The AI Supply Chain Crisis Behind Open-Source CVEs and API Key Hijacking
Multiple open-source projects disclosed severe CVEs in one day, PHP flaws alarmed the FBI, and Railway and OpenCode were breached with API keys stolen. This article breaks down the vulnerability window, AI-accelerated exploitation, and the AI supply chain crisis behind the API key black market, with a developer defense checklist.