@nudojs/core 0.3.0 → 0.4.0
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/dist/chunk-N7E2A7IK.js +7216 -0
- package/dist/exec.d.ts +2 -0
- package/dist/exec.js +174 -0
- package/dist/index-DgyudAkw.d.ts +911 -0
- package/dist/index.d.ts +64 -710
- package/dist/index.js +3269 -8426
- package/package.json +18 -5
- package/CHANGELOG.md +0 -34
- package/src/__tests__/environment.test.ts +0 -48
- package/src/__tests__/fn-sig-impl.test.ts +0 -57
- package/src/__tests__/ops-refined.test.ts +0 -196
- package/src/__tests__/ops.test.ts +0 -84
- package/src/__tests__/type-value-phase2.test.ts +0 -117
- package/src/__tests__/type-value-phase3.test.ts +0 -94
- package/src/__tests__/type-value-refined.test.ts +0 -210
- package/src/__tests__/type-value.test.ts +0 -319
- package/src/algebra/__tests__/abs-native.test.ts +0 -176
- package/src/algebra/__tests__/arithmetic.test.ts +0 -170
- package/src/algebra/__tests__/ast-eval.test.ts +0 -154
- package/src/algebra/__tests__/bridge.test.ts +0 -160
- package/src/algebra/__tests__/call-budget.test.ts +0 -117
- package/src/algebra/__tests__/check-case-consistency.test.ts +0 -69
- package/src/algebra/__tests__/check-gold.test.ts +0 -290
- package/src/algebra/__tests__/check-json.test.ts +0 -57
- package/src/algebra/__tests__/check-mini-repo.test.ts +0 -49
- package/src/algebra/__tests__/check-real-commander.test.ts +0 -43
- package/src/algebra/__tests__/check-real-packages.test.ts +0 -165
- package/src/algebra/__tests__/check-recall-gold.test.ts +0 -1024
- package/src/algebra/__tests__/check-require-dep-memo.test.ts +0 -45
- package/src/algebra/__tests__/check-return-contract.test.ts +0 -90
- package/src/algebra/__tests__/check-shape-gold.test.ts +0 -303
- package/src/algebra/__tests__/check-source-memo.test.ts +0 -204
- package/src/algebra/__tests__/check.test.ts +0 -69
- package/src/algebra/__tests__/denote.test.ts +0 -77
- package/src/algebra/__tests__/diagnostics.test.ts +0 -64
- package/src/algebra/__tests__/differential.test.ts +0 -124
- package/src/algebra/__tests__/div-mod.test.ts +0 -38
- package/src/algebra/__tests__/example0.test.ts +0 -103
- package/src/algebra/__tests__/exec-analyze-throws.test.ts +0 -60
- package/src/algebra/__tests__/exec-async.test.ts +0 -46
- package/src/algebra/__tests__/exec-bpath.test.ts +0 -193
- package/src/algebra/__tests__/exec-class.test.ts +0 -56
- package/src/algebra/__tests__/exec-e2e.test.ts +0 -97
- package/src/algebra/__tests__/exec-generator.test.ts +0 -53
- package/src/algebra/__tests__/exec-inherit-default.test.ts +0 -111
- package/src/algebra/__tests__/exec-optional-nested.test.ts +0 -87
- package/src/algebra/__tests__/exec-rest.test.ts +0 -55
- package/src/algebra/__tests__/exec-spread-forof.test.ts +0 -85
- package/src/algebra/__tests__/exec-static-computed.test.ts +0 -61
- package/src/algebra/__tests__/exec-switch.test.ts +0 -74
- package/src/algebra/__tests__/exec-trycatch.test.ts +0 -71
- package/src/algebra/__tests__/fn-fp-memo.test.ts +0 -166
- package/src/algebra/__tests__/fn-fp-multidecl.test.ts +0 -49
- package/src/algebra/__tests__/format.test.ts +0 -34
- package/src/algebra/__tests__/generalize-memo.test.ts +0 -285
- package/src/algebra/__tests__/generalize.test.ts +0 -113
- package/src/algebra/__tests__/hash-source.test.ts +0 -19
- package/src/algebra/__tests__/hof.test.ts +0 -80
- package/src/algebra/__tests__/inlay.test.ts +0 -30
- package/src/algebra/__tests__/language.test.ts +0 -77
- package/src/algebra/__tests__/leq.test.ts +0 -109
- package/src/algebra/__tests__/load-deps-fp-memo.test.ts +0 -132
- package/src/algebra/__tests__/methods.test.ts +0 -37
- package/src/algebra/__tests__/node-types.test.ts +0 -26
- package/src/algebra/__tests__/nudo-constraints.ts +0 -46
- package/src/algebra/__tests__/objects.test.ts +0 -117
- package/src/algebra/__tests__/parse-source-cache.test.ts +0 -42
- package/src/algebra/__tests__/refine-import.test.ts +0 -109
- package/src/algebra/__tests__/stable-source-key-block.test.ts +0 -23
- package/src/algebra/__tests__/stable-source-key.test.ts +0 -31
- package/src/algebra/__tests__/surface.test.ts +0 -41
- package/src/algebra/__tests__/this-inherit.test.ts +0 -130
- package/src/algebra/abs-fn.ts +0 -49
- package/src/algebra/abs-modules.ts +0 -139
- package/src/algebra/abs.ts +0 -206
- package/src/algebra/arithmetic.ts +0 -691
- package/src/algebra/ast-eval.ts +0 -1763
- package/src/algebra/bridge.ts +0 -350
- package/src/algebra/builtins.ts +0 -373
- package/src/algebra/check-report.ts +0 -150
- package/src/algebra/check.ts +0 -1867
- package/src/algebra/constraint.ts +0 -304
- package/src/algebra/denote.ts +0 -166
- package/src/algebra/diagnostics.ts +0 -210
- package/src/algebra/exec/call.ts +0 -29
- package/src/algebra/exec/calls.ts +0 -96
- package/src/algebra/exec/class-registry.ts +0 -29
- package/src/algebra/exec/class.ts +0 -329
- package/src/algebra/exec/index.ts +0 -6
- package/src/algebra/exec/member-diag.ts +0 -141
- package/src/algebra/exec/run.ts +0 -341
- package/src/algebra/exec/runtime.ts +0 -583
- package/src/algebra/exec/transpile.ts +0 -993
- package/src/algebra/fn-fp.ts +0 -294
- package/src/algebra/format.ts +0 -91
- package/src/algebra/generalize.ts +0 -776
- package/src/algebra/hash-source.ts +0 -27
- package/src/algebra/index.ts +0 -34
- package/src/algebra/inlay.ts +0 -198
- package/src/algebra/language.ts +0 -289
- package/src/algebra/leak.ts +0 -68
- package/src/algebra/leq.ts +0 -301
- package/src/algebra/load-deps-fp.ts +0 -97
- package/src/algebra/methods.ts +0 -128
- package/src/algebra/objects.ts +0 -251
- package/src/algebra/parse-source.ts +0 -64
- package/src/algebra/phi.ts +0 -42
- package/src/algebra/pred.ts +0 -395
- package/src/algebra/refine.ts +0 -227
- package/src/algebra/stable-source-key.ts +0 -81
- package/src/algebra/surface.ts +0 -206
- package/src/algebra/template.ts +0 -143
- package/src/algebra/term.ts +0 -119
- package/src/environment.ts +0 -74
- package/src/index.ts +0 -73
- package/src/mock-helpers.ts +0 -166
- package/src/ops.ts +0 -191
- package/src/refinements/range.ts +0 -86
- package/src/refinements/template-predicates.ts +0 -11
- package/src/refinements/template.ts +0 -176
- package/src/strip-types.ts +0 -236
- package/src/type-value.ts +0 -513
- package/tsconfig.json +0 -8
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
T,
|
|
4
|
-
typeValueEquals,
|
|
5
|
-
isSubtypeOf,
|
|
6
|
-
typeValueToString,
|
|
7
|
-
deepCloneTypeValue,
|
|
8
|
-
getPrimitiveTypeOf,
|
|
9
|
-
getRefinedBase,
|
|
10
|
-
} from "../type-value.ts";
|
|
11
|
-
import { createTemplate, isTemplate, getTemplateParts, getKnownPrefix, getKnownSuffix } from "../refinements/template.ts";
|
|
12
|
-
import { createRange, isRange, getRangeMeta } from "../refinements/range.ts";
|
|
13
|
-
|
|
14
|
-
describe("Refined TypeValue", () => {
|
|
15
|
-
describe("T.refine", () => {
|
|
16
|
-
it("creates a refined type", () => {
|
|
17
|
-
const odd = T.refine(T.number, { name: "odd", meta: {} });
|
|
18
|
-
expect(odd.kind).toBe("refined");
|
|
19
|
-
if (odd.kind === "refined") {
|
|
20
|
-
expect(odd.base).toBe(T.number);
|
|
21
|
-
expect(odd.refinement.name).toBe("odd");
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe("typeValueEquals", () => {
|
|
27
|
-
it("equal refined types with same name and base", () => {
|
|
28
|
-
const a = T.refine(T.number, { name: "odd", meta: {} });
|
|
29
|
-
const b = T.refine(T.number, { name: "odd", meta: {} });
|
|
30
|
-
expect(typeValueEquals(a, b)).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("different name means not equal", () => {
|
|
34
|
-
const a = T.refine(T.number, { name: "odd", meta: {} });
|
|
35
|
-
const b = T.refine(T.number, { name: "even", meta: {} });
|
|
36
|
-
expect(typeValueEquals(a, b)).toBe(false);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("different base means not equal", () => {
|
|
40
|
-
const a = T.refine(T.number, { name: "x", meta: {} });
|
|
41
|
-
const b = T.refine(T.string, { name: "x", meta: {} });
|
|
42
|
-
expect(typeValueEquals(a, b)).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe("isSubtypeOf", () => {
|
|
47
|
-
it("refined is subtype of its base", () => {
|
|
48
|
-
const odd = T.refine(T.number, { name: "odd", meta: {} });
|
|
49
|
-
expect(isSubtypeOf(odd, T.number)).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("refined is subtype of unknown", () => {
|
|
53
|
-
const odd = T.refine(T.number, { name: "odd", meta: {} });
|
|
54
|
-
expect(isSubtypeOf(odd, T.unknown)).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("literal is subtype of refined when check passes", () => {
|
|
58
|
-
const odd = T.refine(T.number, {
|
|
59
|
-
name: "odd",
|
|
60
|
-
meta: {},
|
|
61
|
-
check: (v) => typeof v === "number" && v % 2 !== 0,
|
|
62
|
-
});
|
|
63
|
-
expect(isSubtypeOf(T.literal(3), odd)).toBe(true);
|
|
64
|
-
expect(isSubtypeOf(T.literal(4), odd)).toBe(false);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("nested refined is subtype of outer base", () => {
|
|
68
|
-
const integer = T.refine(T.number, { name: "integer", meta: {} });
|
|
69
|
-
const positiveInt = T.refine(integer, { name: "positive integer", meta: {} });
|
|
70
|
-
expect(isSubtypeOf(positiveInt, T.number)).toBe(true);
|
|
71
|
-
expect(isSubtypeOf(positiveInt, integer)).toBe(true);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe("typeValueToString", () => {
|
|
76
|
-
it("uses refinement name", () => {
|
|
77
|
-
const odd = T.refine(T.number, { name: "odd", meta: {} });
|
|
78
|
-
expect(typeValueToString(odd)).toBe("odd");
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe("getPrimitiveTypeOf", () => {
|
|
83
|
-
it("delegates to base", () => {
|
|
84
|
-
const odd = T.refine(T.number, { name: "odd", meta: {} });
|
|
85
|
-
expect(getPrimitiveTypeOf(odd)).toBe("number");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("handles nested refined", () => {
|
|
89
|
-
const inner = T.refine(T.string, { name: "inner", meta: {} });
|
|
90
|
-
const outer = T.refine(inner, { name: "outer", meta: {} });
|
|
91
|
-
expect(getPrimitiveTypeOf(outer)).toBe("string");
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe("getRefinedBase", () => {
|
|
96
|
-
it("unwraps to primitive", () => {
|
|
97
|
-
const inner = T.refine(T.number, { name: "a", meta: {} });
|
|
98
|
-
const outer = T.refine(inner, { name: "b", meta: {} });
|
|
99
|
-
expect(getRefinedBase(outer)).toBe(T.number);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("returns non-refined as-is", () => {
|
|
103
|
-
expect(getRefinedBase(T.string)).toBe(T.string);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe("deepCloneTypeValue", () => {
|
|
108
|
-
it("clones refined type", () => {
|
|
109
|
-
const odd = T.refine(T.number, { name: "odd", meta: { x: 1 } });
|
|
110
|
-
const cloned = deepCloneTypeValue(odd);
|
|
111
|
-
expect(typeValueEquals(cloned, odd)).toBe(true);
|
|
112
|
-
expect(cloned).not.toBe(odd);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe("Template String", () => {
|
|
118
|
-
it("creates template from parts", () => {
|
|
119
|
-
const tmpl = createTemplate([T.literal("xy"), T.string]);
|
|
120
|
-
expect(isTemplate(tmpl)).toBe(true);
|
|
121
|
-
expect(tmpl.kind).toBe("refined");
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("collapses to literal when all parts are literal", () => {
|
|
125
|
-
const tmpl = createTemplate([T.literal("hello"), T.literal(" world")]);
|
|
126
|
-
expect(tmpl.kind).toBe("literal");
|
|
127
|
-
if (tmpl.kind === "literal") expect(tmpl.value).toBe("hello world");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("collapses to T.string when only T.string", () => {
|
|
131
|
-
const tmpl = createTemplate([T.string]);
|
|
132
|
-
expect(tmpl.kind).toBe("primitive");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("getKnownPrefix extracts leading literals", () => {
|
|
136
|
-
expect(getKnownPrefix([T.literal("xy"), T.string])).toBe("xy");
|
|
137
|
-
expect(getKnownPrefix([T.string, T.literal("z")])).toBe("");
|
|
138
|
-
expect(getKnownPrefix([T.literal("a"), T.literal("b"), T.string])).toBe("ab");
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("getKnownSuffix extracts trailing literals", () => {
|
|
142
|
-
expect(getKnownSuffix([T.string, T.literal("!")])).toBe("!");
|
|
143
|
-
expect(getKnownSuffix([T.literal("x"), T.string])).toBe("");
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("getTemplateParts returns parts", () => {
|
|
147
|
-
const tmpl = createTemplate([T.literal("x"), T.string]);
|
|
148
|
-
const parts = getTemplateParts(tmpl);
|
|
149
|
-
expect(parts).toHaveLength(2);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("toString formats as template literal", () => {
|
|
153
|
-
const tmpl = createTemplate([T.literal("xy"), T.string]);
|
|
154
|
-
expect(typeValueToString(tmpl)).toBe("`xy${string}`");
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("is subtype of T.string", () => {
|
|
158
|
-
const tmpl = createTemplate([T.literal("x"), T.string]);
|
|
159
|
-
expect(isSubtypeOf(tmpl, T.string)).toBe(true);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it("check validates concrete strings", () => {
|
|
163
|
-
const tmpl = createTemplate([T.literal("xy"), T.string]);
|
|
164
|
-
if (tmpl.kind === "refined" && tmpl.refinement.check) {
|
|
165
|
-
expect(tmpl.refinement.check("xyhello")).toBe(true);
|
|
166
|
-
expect(tmpl.refinement.check("xy")).toBe(true);
|
|
167
|
-
expect(tmpl.refinement.check("ahello")).toBe(false);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe("Range", () => {
|
|
173
|
-
it("creates range type", () => {
|
|
174
|
-
const r = createRange({ min: 0 });
|
|
175
|
-
expect(isRange(r)).toBe(true);
|
|
176
|
-
expect(r.kind).toBe("refined");
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it("collapses to literal when min === max", () => {
|
|
180
|
-
const r = createRange({ min: 5, max: 5 });
|
|
181
|
-
expect(r.kind).toBe("literal");
|
|
182
|
-
if (r.kind === "literal") expect(r.value).toBe(5);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("getRangeMeta returns metadata", () => {
|
|
186
|
-
const r = createRange({ min: 0, max: 100, integer: true });
|
|
187
|
-
const meta = getRangeMeta(r);
|
|
188
|
-
expect(meta).toEqual({ min: 0, max: 100, integer: true });
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("is subtype of T.number", () => {
|
|
192
|
-
const r = createRange({ min: 0 });
|
|
193
|
-
expect(isSubtypeOf(r, T.number)).toBe(true);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it("toString formats with constraints", () => {
|
|
197
|
-
expect(typeValueToString(createRange({ min: 0 }))).toBe("number (>= 0)");
|
|
198
|
-
expect(typeValueToString(createRange({ min: 0, max: 100 }))).toBe("number (>= 0, <= 100)");
|
|
199
|
-
expect(typeValueToString(createRange({ integer: true }))).toBe("integer");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("check validates concrete numbers", () => {
|
|
203
|
-
const r = createRange({ min: 0, max: 10 });
|
|
204
|
-
if (r.kind === "refined" && r.refinement.check) {
|
|
205
|
-
expect(r.refinement.check(5)).toBe(true);
|
|
206
|
-
expect(r.refinement.check(-1)).toBe(false);
|
|
207
|
-
expect(r.refinement.check(11)).toBe(false);
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
});
|
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
T,
|
|
4
|
-
type LiteralValue,
|
|
5
|
-
type TypeValue,
|
|
6
|
-
typeValueEquals,
|
|
7
|
-
simplifyUnion,
|
|
8
|
-
widenLiteral,
|
|
9
|
-
collapseLiteralUnion,
|
|
10
|
-
isSubtypeOf,
|
|
11
|
-
typeValueToString,
|
|
12
|
-
narrowType,
|
|
13
|
-
subtractType,
|
|
14
|
-
getPrimitiveTypeOf,
|
|
15
|
-
} from "../type-value.ts";
|
|
16
|
-
import { createTemplate } from "../refinements/template.ts";
|
|
17
|
-
|
|
18
|
-
describe("T factory", () => {
|
|
19
|
-
it("creates literal type values", () => {
|
|
20
|
-
expect(T.literal(1)).toEqual({ kind: "literal", value: 1 });
|
|
21
|
-
expect(T.literal("hello")).toEqual({ kind: "literal", value: "hello" });
|
|
22
|
-
expect(T.literal(true)).toEqual({ kind: "literal", value: true });
|
|
23
|
-
expect(T.literal(null)).toEqual({ kind: "literal", value: null });
|
|
24
|
-
expect(T.literal(undefined)).toEqual({ kind: "literal", value: undefined });
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("provides primitive singletons", () => {
|
|
28
|
-
expect(T.number).toEqual({ kind: "primitive", type: "number" });
|
|
29
|
-
expect(T.string).toEqual({ kind: "primitive", type: "string" });
|
|
30
|
-
expect(T.boolean).toEqual({ kind: "primitive", type: "boolean" });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("creates object type values", () => {
|
|
34
|
-
const obj = T.object({ x: T.number, y: T.string });
|
|
35
|
-
expect(obj.kind).toBe("object");
|
|
36
|
-
if (obj.kind === "object") {
|
|
37
|
-
expect(obj.properties.x).toBe(T.number);
|
|
38
|
-
expect(obj.properties.y).toBe(T.string);
|
|
39
|
-
expect(typeof obj.id).toBe("symbol");
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("creates union type values with simplification", () => {
|
|
44
|
-
const u = T.union(T.literal(1), T.literal(2));
|
|
45
|
-
expect(u.kind).toBe("union");
|
|
46
|
-
if (u.kind === "union") {
|
|
47
|
-
expect(u.members).toHaveLength(2);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("simplifies single-member union to the member", () => {
|
|
52
|
-
const u = T.union(T.number);
|
|
53
|
-
expect(u).toBe(T.number);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("simplifies empty union to never", () => {
|
|
57
|
-
const u = T.union(T.never, T.never);
|
|
58
|
-
expect(u).toEqual(T.never);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe("typeValueEquals", () => {
|
|
63
|
-
it("compares literals", () => {
|
|
64
|
-
expect(typeValueEquals(T.literal(1), T.literal(1))).toBe(true);
|
|
65
|
-
expect(typeValueEquals(T.literal(1), T.literal(2))).toBe(false);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("compares primitives", () => {
|
|
69
|
-
expect(typeValueEquals(T.number, T.number)).toBe(true);
|
|
70
|
-
expect(typeValueEquals(T.number, T.string)).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("compares never and unknown", () => {
|
|
74
|
-
expect(typeValueEquals(T.never, T.never)).toBe(true);
|
|
75
|
-
expect(typeValueEquals(T.unknown, T.unknown)).toBe(true);
|
|
76
|
-
expect(typeValueEquals(T.never, T.unknown)).toBe(false);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe("simplifyUnion", () => {
|
|
81
|
-
it("deduplicates members", () => {
|
|
82
|
-
const result = simplifyUnion([T.literal(1), T.literal(1), T.literal(2)]);
|
|
83
|
-
expect(result.kind).toBe("union");
|
|
84
|
-
if (result.kind === "union") {
|
|
85
|
-
expect(result.members).toHaveLength(2);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("flattens nested unions", () => {
|
|
90
|
-
const inner = T.union(T.literal(1), T.literal(2));
|
|
91
|
-
const result = simplifyUnion([inner, T.literal(3)]);
|
|
92
|
-
expect(result.kind).toBe("union");
|
|
93
|
-
if (result.kind === "union") {
|
|
94
|
-
expect(result.members).toHaveLength(3);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("absorbs unknown", () => {
|
|
99
|
-
const result = simplifyUnion([T.number, T.unknown]);
|
|
100
|
-
expect(result).toEqual(T.unknown);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("removes never", () => {
|
|
104
|
-
const result = simplifyUnion([T.number, T.never]);
|
|
105
|
-
expect(result).toBe(T.number);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe("widenLiteral", () => {
|
|
110
|
-
it("widens number literal to number", () => {
|
|
111
|
-
expect(widenLiteral(T.literal(42))).toEqual(T.number);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("widens string literal to string", () => {
|
|
115
|
-
expect(widenLiteral(T.literal("hi"))).toEqual(T.string);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("widens boolean literal to boolean", () => {
|
|
119
|
-
expect(widenLiteral(T.literal(true))).toEqual(T.boolean);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("returns non-literal unchanged", () => {
|
|
123
|
-
expect(widenLiteral(T.number)).toBe(T.number);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("collapses 20 number literals beyond maxLiterals to number", () => {
|
|
127
|
-
const members = Array.from({ length: 20 }, (_, i) => T.literal((i + 1) ** 2));
|
|
128
|
-
const u = { kind: "union", members } as const;
|
|
129
|
-
const result = collapseLiteralUnion(u, 4);
|
|
130
|
-
expect(result).toEqual(T.number);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("collapses 5 string literals to string", () => {
|
|
134
|
-
const u = {
|
|
135
|
-
kind: "union",
|
|
136
|
-
members: ["a", "b", "c", "d", "e"].map((s) => T.literal(s)),
|
|
137
|
-
} as const;
|
|
138
|
-
const result = collapseLiteralUnion(u, 4);
|
|
139
|
-
expect(result).toEqual(T.string);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("returns union unchanged when member count equals maxLiterals", () => {
|
|
143
|
-
const u: TypeValue = {
|
|
144
|
-
kind: "union",
|
|
145
|
-
members: [T.literal(1), T.literal(2), T.literal(3), T.literal(4)],
|
|
146
|
-
};
|
|
147
|
-
expect(collapseLiteralUnion(u, 4)).toBe(u);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("mixed union: number literals absorbed by T.number, string literal kept (行为已修复:吸收律)", () => {
|
|
151
|
-
const u: TypeValue = {
|
|
152
|
-
kind: "union",
|
|
153
|
-
members: [T.literal(1), T.number, T.literal("x"), T.literal(2), T.literal(3)],
|
|
154
|
-
};
|
|
155
|
-
// 原断言期望原样返回 u;现 collapseLiteralUnion 复用 simplifyUnion 的吸收律,
|
|
156
|
-
// 数字字面量 1/2/3 被共存的 T.number 吸收,仅剩 ["x", number](数量 ≤ 阈值不再坍缩)
|
|
157
|
-
expect(collapseLiteralUnion(u, 4)).toEqual(T.union(T.number, T.literal("x")));
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("returns non-union unchanged (single literal / T.number)", () => {
|
|
161
|
-
const lit = T.literal(1);
|
|
162
|
-
expect(collapseLiteralUnion(lit, 0)).toBe(lit);
|
|
163
|
-
expect(collapseLiteralUnion(T.number, 0)).toBe(T.number);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe("isSubtypeOf", () => {
|
|
168
|
-
it("everything is subtype of unknown", () => {
|
|
169
|
-
expect(isSubtypeOf(T.number, T.unknown)).toBe(true);
|
|
170
|
-
expect(isSubtypeOf(T.literal(1), T.unknown)).toBe(true);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("never is subtype of everything", () => {
|
|
174
|
-
expect(isSubtypeOf(T.never, T.number)).toBe(true);
|
|
175
|
-
expect(isSubtypeOf(T.never, T.never)).toBe(true);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("literal is subtype of its primitive", () => {
|
|
179
|
-
expect(isSubtypeOf(T.literal(1), T.number)).toBe(true);
|
|
180
|
-
expect(isSubtypeOf(T.literal("hi"), T.string)).toBe(true);
|
|
181
|
-
expect(isSubtypeOf(T.literal(true), T.boolean)).toBe(true);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("literal is not subtype of wrong primitive", () => {
|
|
185
|
-
expect(isSubtypeOf(T.literal(1), T.string)).toBe(false);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it("union is subtype if all members are", () => {
|
|
189
|
-
const u = T.union(T.literal(1), T.literal(2));
|
|
190
|
-
expect(isSubtypeOf(u, T.number)).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("value is subtype of union containing it", () => {
|
|
194
|
-
const u = T.union(T.number, T.string);
|
|
195
|
-
expect(isSubtypeOf(T.number, u)).toBe(true);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
describe("typeValueToString", () => {
|
|
200
|
-
it("formats literals", () => {
|
|
201
|
-
expect(typeValueToString(T.literal(1))).toBe("1");
|
|
202
|
-
expect(typeValueToString(T.literal("hi"))).toBe('"hi"');
|
|
203
|
-
expect(typeValueToString(T.literal(true))).toBe("true");
|
|
204
|
-
expect(typeValueToString(T.literal(null))).toBe("null");
|
|
205
|
-
expect(typeValueToString(T.literal(undefined))).toBe("undefined");
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it("formats primitives", () => {
|
|
209
|
-
expect(typeValueToString(T.number)).toBe("number");
|
|
210
|
-
expect(typeValueToString(T.string)).toBe("string");
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("formats unions", () => {
|
|
214
|
-
const u = T.union(T.number, T.string);
|
|
215
|
-
expect(typeValueToString(u)).toBe("number | string");
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it("formats objects", () => {
|
|
219
|
-
const obj = T.object({ x: T.number });
|
|
220
|
-
expect(typeValueToString(obj)).toBe("{ x: number }");
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it("formats never and unknown", () => {
|
|
224
|
-
expect(typeValueToString(T.never)).toBe("never");
|
|
225
|
-
expect(typeValueToString(T.unknown)).toBe("unknown");
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
describe("narrowType", () => {
|
|
230
|
-
it("narrows union by predicate", () => {
|
|
231
|
-
const u = T.union(T.literal(1), T.literal("a"), T.literal(true));
|
|
232
|
-
const result = narrowType(u, (m) => m.kind === "literal" && typeof m.value === "number");
|
|
233
|
-
expect(typeValueEquals(result, T.literal(1))).toBe(true);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it("returns never if nothing matches", () => {
|
|
237
|
-
const result = narrowType(T.number, (m) => m.kind === "literal");
|
|
238
|
-
expect(result).toEqual(T.never);
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
describe("getPrimitiveTypeOf", () => {
|
|
243
|
-
it("returns typeof for literals", () => {
|
|
244
|
-
expect(getPrimitiveTypeOf(T.literal(1))).toBe("number");
|
|
245
|
-
expect(getPrimitiveTypeOf(T.literal("hi"))).toBe("string");
|
|
246
|
-
expect(getPrimitiveTypeOf(T.literal(null))).toBe("object");
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it("returns type for primitives", () => {
|
|
250
|
-
expect(getPrimitiveTypeOf(T.number)).toBe("number");
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it("returns object for object/array/tuple", () => {
|
|
254
|
-
expect(getPrimitiveTypeOf(T.object({}))).toBe("object");
|
|
255
|
-
expect(getPrimitiveTypeOf(T.array(T.number))).toBe("object");
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
describe("union absorption law (3 | number → number)", () => {
|
|
260
|
-
it("absorbs number literal into number", () => {
|
|
261
|
-
expect(T.union(T.literal(3), T.number)).toBe(T.number);
|
|
262
|
-
expect(T.union(T.number, T.literal(3))).toBe(T.number);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it("absorbs string literal into string", () => {
|
|
266
|
-
expect(T.union(T.literal("a"), T.string)).toBe(T.string);
|
|
267
|
-
expect(T.union(T.string, T.literal("a"))).toBe(T.string);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it("absorbs boolean literal into boolean", () => {
|
|
271
|
-
expect(T.union(T.literal(true), T.boolean)).toBe(T.boolean);
|
|
272
|
-
expect(T.union(T.boolean, T.literal(false))).toBe(T.boolean);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it("absorbs bigint literal into bigint", () => {
|
|
276
|
-
// LiteralValue 类型暂不含 bigint,运行时以 cast 构造
|
|
277
|
-
expect(T.union(T.literal(10n as unknown as LiteralValue), T.bigint)).toBe(T.bigint);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it("absorbs template literal into string", () => {
|
|
281
|
-
const tmpl = createTemplate([T.literal("x"), T.string]);
|
|
282
|
-
expect(T.union(tmpl, T.string)).toBe(T.string);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it("absorbs only matching base across mixed members (order-stable)", () => {
|
|
286
|
-
// 1 被 number 吸收,"a" 无 string 在场而保留
|
|
287
|
-
const u = T.union(T.literal(1), T.number, T.literal("a"));
|
|
288
|
-
expect(u.kind).toBe("union");
|
|
289
|
-
if (u.kind === "union") {
|
|
290
|
-
expect(u.members).toHaveLength(2);
|
|
291
|
-
expect(typeValueEquals(u.members[0], T.number)).toBe(true);
|
|
292
|
-
expect(typeValueEquals(u.members[1], T.literal("a"))).toBe(true);
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it("keeps different-base literal union (1 | \"a\")", () => {
|
|
297
|
-
const u = T.union(T.literal(1), T.literal("a"));
|
|
298
|
-
expect(u.kind).toBe("union");
|
|
299
|
-
if (u.kind === "union") {
|
|
300
|
-
expect(u.members).toHaveLength(2);
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
it("keeps pure literal unions (\"a\" | \"b\", 2 | -9)", () => {
|
|
305
|
-
expect(typeValueToString(T.union(T.literal("a"), T.literal("b")))).toBe('"a" | "b"');
|
|
306
|
-
expect(typeValueToString(T.union(T.literal(2), T.literal(-9)))).toBe("2 | -9");
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it("keeps undefined/null literal unions untouched", () => {
|
|
310
|
-
expect(typeValueToString(T.union(T.undefined, T.string))).toBe("undefined | string");
|
|
311
|
-
expect(typeValueToString(T.union(T.null, T.literal(1)))).toBe("null | 1");
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it("keeps pure primitive unions untouched (number | string | boolean)", () => {
|
|
315
|
-
expect(typeValueToString(T.union(T.number, T.string, T.boolean))).toBe(
|
|
316
|
-
"number | string | boolean",
|
|
317
|
-
);
|
|
318
|
-
});
|
|
319
|
-
});
|