Skip to content

Commit b9ac9c2

Browse files
committed
ts/ielib/index.ts -- new types:
- CreRef = string & {} - Direction = IE<number, "Direction"> - StrRef = IE<number, "StrRef"> - Documented why Scope is not narrowed to a union scripts/ts-update.ts -- generator updates: - TYPE_MAPPING: added areref, creref, strref - PARAM_NAME_TYPES: added Face → Direction, removed dead AreRef entry; changed endsWith to exact match - PARAM_NAME_TYPES override logic added to trigger parser (was action-only) - Both file headers updated with new type imports ts/ielib/bg2/spell.ids.ts: import → import type ts/ielib/package.json: version bump 0.1.4 → 0.2.0 scripts/ts-update.test.ts: 3 new tests for PARAM_NAME_TYPES trigger overrides ts/ielib/bg2/actions.d.ts, triggers.d.ts: regenerated with all new types
1 parent 37e9865 commit b9ac9c2

43 files changed

Lines changed: 297 additions & 164 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

scripts/ts-update.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ describe("parseTriggerParameters", () => {
8888
it("throws on unknown type", () => {
8989
expect(() => parseTriggerParameters("Z:Unknown")).toThrow("Unknown type");
9090
});
91+
92+
it("overrides type for param named Scope via PARAM_NAME_TYPES", () => {
93+
const result = parseTriggerParameters("S:Name*,S:Scope*");
94+
expect(result).toBe("name: string, scope: Scope");
95+
});
96+
97+
it("resolves resref type codes (ItmRef, AreRef, SplRef)", () => {
98+
expect(parseTriggerParameters("ItmRef:Item*")).toBe("item: ItmRef");
99+
expect(parseTriggerParameters("AreRef:Area*")).toBe("area: AreRef");
100+
expect(parseTriggerParameters("SplRef:Spell*")).toBe("spell: SplRef");
101+
});
102+
103+
it("does not override non-matching param names", () => {
104+
const result = parseTriggerParameters("S:MyScope*");
105+
expect(result).toBe("myScope: string");
106+
});
91107
});
92108

93109
describe("extractTriggerBlocks", () => {

scripts/ts-update.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,20 @@ const TYPE_MAPPING: Readonly<Record<string, string>> = {
3737
i: "number",
3838
p: "Point",
3939
a: "Action",
40+
areref: "AreRef",
41+
creref: "CreRef",
4042
itmref: "ItmRef",
4143
splref: "SplRef",
44+
strref: "StrRef",
4245
};
4346

44-
const ACTION_FILE_HEADER = `import type { Action, ObjectPtr, Point, SpellID, SplRef } from "../index";
47+
/** Maps YAML param names to TypeScript types. Checked before type/ids resolution. */
48+
const PARAM_NAME_TYPES: Readonly<Record<string, string>> = {
49+
Scope: "Scope",
50+
Face: "Direction",
51+
};
52+
53+
const ACTION_FILE_HEADER = `import type { Action, AreRef, CreRef, Direction, ObjectPtr, Point, Scope, SpellID, SplRef, StrRef } from "../index";
4554
4655
import type { Align } from "./align.ids";
4756
import type { Animate } from "./animate.ids";
@@ -70,7 +79,7 @@ import type { WeatherID } from "./weather.ids";
7079
7180
`;
7281

73-
const TRIGGER_FILE_HEADER = `import type { ObjectPtr, SpellID, ItmRef } from "../index";
82+
const TRIGGER_FILE_HEADER = `import type { AreRef, ItmRef, ObjectPtr, Scope, SplRef, SpellID } from "../index";
7483
7584
import type { Align } from "./align.ids";
7685
import type { AreaTypeID as AreaType } from "./areatype.ids";
@@ -229,8 +238,10 @@ function generateTypeScriptDeclaration(yamlFilePath: string): string | null {
229238
let paramName = param.name.toLowerCase() === "unused" ? `unused${unusedCount++}` : param.name;
230239
paramName = normalizeParamName(paramName, "lower");
231240

241+
// Priority: param name exact override > ids field > type code
242+
const typeOverride = PARAM_NAME_TYPES[param.name];
232243
// Use explicit empty-string check: ids may be "" which should fall through to TYPE_MAPPING
233-
const paramType = normalizeTypeName(
244+
const paramType = typeOverride ?? normalizeTypeName(
234245
param.ids !== undefined && param.ids !== "" ? param.ids : TYPE_MAPPING[param.type] ?? param.type,
235246
);
236247
paramLines.push(`${paramName}: ${paramType}`);
@@ -342,10 +353,11 @@ export function parseTriggerParameters(params: string): string {
342353
const specificType = parts.length > 1 ? parts[1] : undefined;
343354
const formattedName = normalizeParamName(name, "camelCase");
344355

345-
// Use specific IDS type if present and non-empty, otherwise fall through to TYPE_MAPPING
346-
const rawType = specificType !== undefined && specificType !== ""
356+
// Priority: param name exact override > specific IDS type > base type code
357+
const typeOverride = PARAM_NAME_TYPES[name];
358+
const rawType = typeOverride ?? (specificType !== undefined && specificType !== ""
347359
? specificType
348-
: TYPE_MAPPING[type.toLowerCase()];
360+
: TYPE_MAPPING[type.toLowerCase()]);
349361
if (!rawType) {
350362
throw new Error(`Unknown type: "${type}" or "${specificType}"`);
351363
}

ts/ielib/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
## IElib
22

33
Typescript bindings for Infinity Engine BAF.
4+
5+
### Type branding
6+
7+
Numeric IDS types (e.g. `SpellID`, `Align`, `ClassID`, `Slots`) use branded types via `IE<number, "...">` for nominal type safety. This prevents accidentally passing one kind of ID where another is expected. Custom numeric values can be created with a cast:
8+
9+
```typescript
10+
const mySpell = 42 as SpellID;
11+
```
12+
13+
String resource reference types (`ResRef`, `SplRef`, `ItmRef`) are intentionally **not** branded. Resrefs are almost always raw string literals, and branding would require a cast on every usage (e.g. `"SWORD01" as ItmRef`). Unlike numeric IDS types, there is no finite set of valid resrefs to provide as pre-typed constants.
14+
15+
Engine action functions return a branded `Action` type, which allows `ActionOverride` to enforce that its argument is an actual action call rather than an arbitrary expression.
16+
17+
### IDS type naming
18+
19+
Some IDS types use a `*ID` suffix (`ClassID`, `GenderID`, `KitID`, etc.) while others use bare names (`Align`, `EA`, `State`, etc.). The suffix exists to avoid name clashes with same-named trigger/action functions (e.g. `Class()` trigger vs `ClassID` type). Types without a same-named function use the bare name.

0 commit comments

Comments
 (0)