TypeScript Types: struct ⇒ interface
The TypeScriptTypes rule demonstrates attribute-based triggering and recursive type mapping. It converts C++ structs to TypeScript export interface declarations, handling primitives, std::vector<T>, std::optional<T>, std::shared_ptr<T>, and std::map<K,V> recursively.
What it produces
Section titled “What it produces”struct [[codegen::TypeScriptTypes]] UserProfile { std::string username; uint32_t age; std::vector<std::string> roles; std::optional<std::string> bio;};By default the output lands at api_types.TypeScriptTypes.ts next to the input header (the non-cpp default filename pattern is <stem>.<ruleName><ext>). To centralise the output, set outputDirectory and/or outputNameTemplate in the rule config.
// Generated with Codegen - DO NOT EDIT!!!
// Auto-generated TypeScript types for rule: TypeScriptTypes// Source:
export interface UserProfile { username: string; age: number; roles: Array<string>; bio: string | undefined;}The // Sources: lines in the example’s preamble iterate ctx.entities to list the contributing input files. Each entity in entities has its inputFile available, so the preamble can list the sources that contributed to this output file.
Run:
codegen -i include/api_types.hpp -r .codegen/rules -a TypeScriptTypesType mapping
Section titled “Type mapping”| C++ type | TypeScript type |
|---|---|
bool | boolean |
int, int8_t…int64_t, uint8_t…uint64_t, float, double, size_t | number |
std::string, string, path | string |
std::vector<T> | Array<T> |
std::optional<T> | T | undefined |
std::shared_ptr<T> | T | null |
std::map<K,V>, std::unordered_map<K,V> | Record<K, V> |
| Anything else | Used as-is (assumed to be a TypeScript type reference) |
The mapping is implemented as a recursive cppToTs(tSig) function in transform.luau. Adding new mappings means editing only that function.
Extending the type map
Section titled “Extending the type map”Open transform.luau and add entries to the primitives table or new if name == "..." branches for generics:
-- Add std::set<T> -> Set<T>if name == "set" then local args = tSig.identifier.templateArguments or {} if args[1] then return "Set<" .. cppToTs(args[1]) .. ">" end return "Set<unknown>"endFan-in variant
Section titled “Fan-in variant”To consolidate every TypeScript interface into a single types.ts, add a grouping script and (optionally) override the output path:
return function(input) local data = json.decode(input) local result = {} for _, ent in ipairs(data.entities) do result[tostring(ent.registryId)] = "generated/types.ts" end return json.encode(result)endThe output path is resolved against the project root (the working directory).
- Attribute annotation (
[[codegen::TypeScriptTypes]]) triggers struct-level rules; no anchor needed. - Default non-cpp output:
<stem>.<ruleName><ext>next to the input. Override withoutputDirectory/outputNameTemplate. - Type mapping is a recursive LuaU function; extend it by adding branches.
- Adding
grouping.luauswitches from 1:1 to fan-in routing. - Preamble runs once per (rule, outputFile) pair and receives the
entitiesbound to that file, so it can conditionally emit content based on what entities land there.