@mnemonica/tactica 0.3.11 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/lib/analyzer.d.ts +8 -0
- package/lib/analyzer.js +65 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -490,6 +490,8 @@ The analyzer infers property types from constructor bodies and class members. Su
|
|
|
490
490
|
|
|
491
491
|
An existing annotation always beats a weaker inference: `unknown` (and `unknown`-bearing guesses like `Array<unknown>`) never overwrites a known type — `Record<string, unknown>` IS a known type — and an overwrite never drops an optionality modifier.
|
|
492
492
|
|
|
493
|
+
**Method/member return annotations** follow an emission law (0.2.0 behavior, restored after the F10 bare-name ban overshot): an annotation — or any type argument inside it — that resolves to a mnemonica graph type emits the generated alias (`SomeEntry[]` → `Array<EDSRoot_SomeEntry>`; generated-alias spellings resolve via the underscore→dotted law). `InstanceType<typeof X>` — directly or through a local alias (`XInstance = InstanceType<typeof X>`) — drops the wrapper and emits the generated alias (`Definitions_DefinitionEntry`), since the alias already IS the instance type; when X does not resolve to a graph type the WHOLE expression degrades to `unknown` — `InstanceType<unknown>` is invalid TypeScript (TS2344) and is never emitted. An outer generic that is a non-graph **ambient/lib** construct (`MapIterator`, lib helpers — not declared in any analyzed project file) is emitted **verbatim** with inner graph aliases resolved, since it needs no import in the generated file. A **project-local** non-graph name stays `unknown` — the self-contained `types.ts` can carry neither the bare name nor an import (ambiguity between same-named project declarations is the fatal class, as elsewhere). This law covers method/member return annotations; constructor-signature parameters keep their accepted mirror policy.
|
|
494
|
+
|
|
493
495
|
When inference fails the property's type falls back to `unknown` (or `any` in some Topologica paths) — safe, and you can refine manually.
|
|
494
496
|
|
|
495
497
|
### Referenced types in constructor signatures
|
|
@@ -960,7 +962,7 @@ Mnemonica accepts the `exposeInstanceMethods` option at runtime, but tactica's `
|
|
|
960
962
|
|
|
961
963
|
The analyzer does not use `ts.Program.getTypeChecker()` for resolution. Plain TypeScript references in constructor signatures resolve through the importing file's import statements first, then its local declarations, and only then a unique same-named declaration across the scanned files — so a reference whose name is genuinely absent (or lives behind unresolvable specifiers) resolves to `unknown`, while a name several of your own files declare with no import to disambiguate fails hard (see above). References to mnemonica graph types follow the path-aware lookup law described above and fail hard instead of degrading to `unknown`.
|
|
962
964
|
|
|
963
|
-
Two expansion boundaries stay name-based and deliberate: **heritage** — `extends` chains are walked through the same import-aware machinery (depth-capped, cycle-guarded), but bases that resolve to an external/node_modules declaration or to a non-identifier expression (mixin calls like `extends mixin(X)`) are not followed — their inherited fields simply stay absent, same as before the walk existed. And **`typeof` fields** — only module-level const arrays with statically visible literal elements expand to the literal union, with the index suffix consumed entirely (`typeof statusList[number]` emits exactly `'active' | 'not_active' | 'hold'`, never `'hold'[number]`). The value-level twin — element access over a const-asserted literal array (`(<const>[…])[0]`, `([…] as const)[1]`, tracked `x[0]`) — infers the element literal under the same rules; non-numeric, out-of-range, spread, or non-literal elements and general `<T>` assertions (as opposed to `<const>`) stay `unknown`. Any assertion spelling is tracked (`[…] as const`, `satisfies`, `<const>[…]`), and signed numeric literals keep their sign (`-1 | 1`). Cross-file only when the const is imported by the declaring file. Every other `typeof` source — and any indexed-access target that fails to resolve — degrades the whole expression to `unknown`: an index suffix glued onto a fallback type (`unknown[number]`) is invalid TypeScript and is never emitted.
|
|
965
|
+
Two expansion boundaries stay name-based and deliberate: **heritage** — `extends` chains are walked through the same import-aware machinery (depth-capped, cycle-guarded), but bases that resolve to an external/node_modules declaration or to a non-identifier expression (mixin calls like `extends mixin(X)`) are not followed — their inherited fields simply stay absent, same as before the walk existed. And **`typeof` fields** — only module-level const arrays with statically visible literal elements expand to the literal union, with the index suffix consumed entirely (`typeof statusList[number]` emits exactly `'active' | 'not_active' | 'hold'`, never `'hold'[number]`). The value-level twin — element access over a const-asserted literal array (`(<const>[…])[0]`, `([…] as const)[1]`, tracked `x[0]`) — infers the element literal under the same rules; non-numeric, out-of-range, spread, or non-literal elements and general `<T>` assertions (as opposed to `<const>`) stay `unknown`. Any assertion spelling is tracked (`[…] as const`, `satisfies`, `<const>[…]`), and signed numeric literals keep their sign (`-1 | 1`). Cross-file only when the const is imported by the declaring file. Every other `typeof` source — and any indexed-access target that fails to resolve — degrades the whole expression to `unknown`: an index suffix glued onto a fallback type (`unknown[number]`) is invalid TypeScript and is never emitted. A sibling emission law governs method/member return annotations (0.2.0 behavior restored after the F10 bare-name ban overshot): inner type arguments resolving to graph types emit the generated aliases; `InstanceType<typeof X>` (directly or via a local alias) drops the wrapper and emits the alias — X unresolvable degrades the WHOLE expression to `unknown`, never `InstanceType<unknown>` (invalid TS, TS2344); a non-graph **ambient/lib** outer generic (`MapIterator`, lib helpers — not declared in any analyzed project file) emits verbatim with inner aliases resolved; a **project-local** non-graph name stays `unknown` (no imports in the generated file; same-named ambiguity is the fatal class). Constructor-signature parameters keep their accepted mirror policy and are not covered.
|
|
964
966
|
|
|
965
967
|
### Same-file interface merging is last-wins
|
|
966
968
|
|
package/lib/analyzer.d.ts
CHANGED
|
@@ -776,6 +776,14 @@ export declare class MnemonicaAnalyzer {
|
|
|
776
776
|
* metadata heuristic, not the identity-law surface.
|
|
777
777
|
*/
|
|
778
778
|
private resolveWrapInstanceTypePath;
|
|
779
|
+
/**
|
|
780
|
+
* Emission-law helper (0.2.0 restoration): is `name` declared in any
|
|
781
|
+
* ANALYZED PROJECT file? External/ambient files (.d.ts, node_modules)
|
|
782
|
+
* do not count. A name with no project declaration is an ambient/lib
|
|
783
|
+
* construct — safe to emit verbatim into the self-contained types.ts;
|
|
784
|
+
* a project-local name is not (no imports in the generated file).
|
|
785
|
+
*/
|
|
786
|
+
private isProjectDeclaredTypeName;
|
|
779
787
|
/**
|
|
780
788
|
* F24: resolve a bare-identifier annotation to a graph fullPath. The
|
|
781
789
|
* annotation may name the type directly (`LedgerUpdate`) or carry
|