@tilua/parser 1.0.0 → 1.0.2
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 +702 -702
- package/dist/index.cjs +125 -30
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +125 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4395,7 +4395,12 @@ function substitute(t, subst) {
|
|
|
4395
4395
|
return t.name && r.kind === "intersection" && !r.name ? { ...r, name: t.name } : r;
|
|
4396
4396
|
}
|
|
4397
4397
|
case "genericRef":
|
|
4398
|
-
return {
|
|
4398
|
+
return {
|
|
4399
|
+
kind: "genericRef",
|
|
4400
|
+
name: t.name,
|
|
4401
|
+
typeArguments: t.typeArguments.map((a) => substitute(a, subst)),
|
|
4402
|
+
...t.origin ? { origin: t.origin } : {}
|
|
4403
|
+
};
|
|
4399
4404
|
case "keyof":
|
|
4400
4405
|
return { kind: "keyof", target: substitute(t.target, subst) };
|
|
4401
4406
|
case "templateLiteral":
|
|
@@ -4555,6 +4560,7 @@ function optional(t) {
|
|
|
4555
4560
|
return union([t, nilType]);
|
|
4556
4561
|
}
|
|
4557
4562
|
function widen(t) {
|
|
4563
|
+
if (isAliasNamed(t)) return t;
|
|
4558
4564
|
switch (t.kind) {
|
|
4559
4565
|
case "literal":
|
|
4560
4566
|
return primitive(t.base);
|
|
@@ -4594,7 +4600,7 @@ function isAssignable(rawA, rawB) {
|
|
|
4594
4600
|
if (expandAlias) {
|
|
4595
4601
|
if (a.kind === "genericRef" && b.kind !== "genericRef") a = expandAlias(a);
|
|
4596
4602
|
else if (b.kind === "genericRef" && a.kind !== "genericRef") b = expandAlias(b);
|
|
4597
|
-
else if (a.kind === "genericRef" && b.kind === "genericRef" && a
|
|
4603
|
+
else if (a.kind === "genericRef" && b.kind === "genericRef" && !sameRefTarget(a, b)) {
|
|
4598
4604
|
a = expandAlias(a);
|
|
4599
4605
|
b = expandAlias(b);
|
|
4600
4606
|
}
|
|
@@ -4704,10 +4710,17 @@ function isAssignableInner(a, b) {
|
|
|
4704
4710
|
}
|
|
4705
4711
|
if (b.kind === "typeParam") return false;
|
|
4706
4712
|
if (a.kind === "genericRef" || b.kind === "genericRef") {
|
|
4707
|
-
return a.kind === "genericRef" && b.kind === "genericRef" && a
|
|
4713
|
+
return a.kind === "genericRef" && b.kind === "genericRef" && sameRefTarget(a, b) && a.typeArguments.length === b.typeArguments.length && a.typeArguments.every((x, i) => equalTypes(x, b.typeArguments[i]));
|
|
4708
4714
|
}
|
|
4709
4715
|
return false;
|
|
4710
4716
|
}
|
|
4717
|
+
function isAliasNamed(t) {
|
|
4718
|
+
if (t.kind === "object" || t.kind === "intersection") return t.name !== void 0;
|
|
4719
|
+
return t.alias !== void 0;
|
|
4720
|
+
}
|
|
4721
|
+
function sameRefTarget(a, b) {
|
|
4722
|
+
return a.name === b.name && (!a.origin || !b.origin || a.origin === b.origin);
|
|
4723
|
+
}
|
|
4711
4724
|
function isNoValue(t) {
|
|
4712
4725
|
return t.kind === "tuple" && t.elements.length === 0;
|
|
4713
4726
|
}
|
|
@@ -5108,6 +5121,7 @@ function mergeObjectMembers(types) {
|
|
|
5108
5121
|
var INTERNAL_MEMBERS = /* @__PURE__ */ new Set(["ClassObject", "ParentClass"]);
|
|
5109
5122
|
var MAX_MISSING = 3;
|
|
5110
5123
|
var MAX_EXPLANATION = 90;
|
|
5124
|
+
var MAX_TYPE = 120;
|
|
5111
5125
|
function analyzeTypes(program, scopes, options = {}) {
|
|
5112
5126
|
return new TypeAnalyzer(program, scopes, options).run();
|
|
5113
5127
|
}
|
|
@@ -5486,11 +5500,15 @@ var TypeAnalyzer = class {
|
|
|
5486
5500
|
narrowedTypeOf = /* @__PURE__ */ new Map();
|
|
5487
5501
|
typeOfTypeNode = /* @__PURE__ */ new Map();
|
|
5488
5502
|
expectedTypeOf = /* @__PURE__ */ new Map();
|
|
5503
|
+
iteratesValues = /* @__PURE__ */ new Set();
|
|
5489
5504
|
/** Public: each alias resolved once (generic aliases keep their params as
|
|
5490
5505
|
* `typeParam` nodes in the body). */
|
|
5491
5506
|
aliases = new AliasMap();
|
|
5492
5507
|
/** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
|
|
5493
5508
|
aliasDefs = /* @__PURE__ */ new Map();
|
|
5509
|
+
/** Put on every ref to one of `aliasDefs`, so a module that imports the
|
|
5510
|
+
* type expands the ref here rather than by its name there. */
|
|
5511
|
+
origin = { expand: (t) => this.expand(t) };
|
|
5494
5512
|
/** See `resolveClass`. */
|
|
5495
5513
|
classTypes = /* @__PURE__ */ new WeakMap();
|
|
5496
5514
|
/** See `instanceType` — one instance type per `class ... end`. */
|
|
@@ -5566,6 +5584,7 @@ var TypeAnalyzer = class {
|
|
|
5566
5584
|
typeOfTypeNode: this.typeOfTypeNode,
|
|
5567
5585
|
expectedTypeOf: this.expectedTypeOf,
|
|
5568
5586
|
aliases: this.resolveDeferredAliases(),
|
|
5587
|
+
iteratesValues: this.iteratesValues,
|
|
5569
5588
|
diagnostics: this.diagnostics
|
|
5570
5589
|
};
|
|
5571
5590
|
}
|
|
@@ -5898,7 +5917,8 @@ var TypeAnalyzer = class {
|
|
|
5898
5917
|
return {
|
|
5899
5918
|
kind: "genericRef",
|
|
5900
5919
|
name: stmt.name.name,
|
|
5901
|
-
typeArguments: params.map((p) => typeParam(p.name, p.constraint ? this.resolveType(p.constraint) : void 0))
|
|
5920
|
+
typeArguments: params.map((p) => typeParam(p.name, p.constraint ? this.resolveType(p.constraint) : void 0)),
|
|
5921
|
+
origin: this.origin
|
|
5902
5922
|
};
|
|
5903
5923
|
}
|
|
5904
5924
|
/** The class table: the statics, what it inherits from the class it
|
|
@@ -6059,7 +6079,7 @@ var TypeAnalyzer = class {
|
|
|
6059
6079
|
if (declared && this.emitDiagnostics && !isAssignable(actual, declared)) {
|
|
6060
6080
|
this.diagnostics.push({
|
|
6061
6081
|
node: member.init,
|
|
6062
|
-
message: `Type '${
|
|
6082
|
+
message: `Type '${briefType(actual)}' is not assignable to type '${briefType(declared)}'` + this.explainMismatch(actual, declared)
|
|
6063
6083
|
});
|
|
6064
6084
|
}
|
|
6065
6085
|
continue;
|
|
@@ -6246,7 +6266,7 @@ var TypeAnalyzer = class {
|
|
|
6246
6266
|
if (fits) return;
|
|
6247
6267
|
this.diagnostics.push({
|
|
6248
6268
|
node: stmt,
|
|
6249
|
-
message: `Type '${
|
|
6269
|
+
message: `Type '${briefType(actual)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(actual, declared)
|
|
6250
6270
|
});
|
|
6251
6271
|
}
|
|
6252
6272
|
/** A function that declared what it returns but never does. Only a body
|
|
@@ -6463,7 +6483,8 @@ var TypeAnalyzer = class {
|
|
|
6463
6483
|
return this.expand({
|
|
6464
6484
|
kind: "genericRef",
|
|
6465
6485
|
name: node.base,
|
|
6466
|
-
typeArguments: node.typeArguments.map((a) => this.resolveType(a))
|
|
6486
|
+
typeArguments: node.typeArguments.map((a) => this.resolveType(a)),
|
|
6487
|
+
origin: this.origin
|
|
6467
6488
|
});
|
|
6468
6489
|
}
|
|
6469
6490
|
const imported = this.importedTypes.get(node.base);
|
|
@@ -6476,7 +6497,8 @@ var TypeAnalyzer = class {
|
|
|
6476
6497
|
return this.expand({
|
|
6477
6498
|
kind: "genericRef",
|
|
6478
6499
|
name,
|
|
6479
|
-
typeArguments: node.typeArguments.map((a) => this.resolveType(a))
|
|
6500
|
+
typeArguments: node.typeArguments.map((a) => this.resolveType(a)),
|
|
6501
|
+
origin: this.origin
|
|
6480
6502
|
});
|
|
6481
6503
|
}
|
|
6482
6504
|
return {
|
|
@@ -6906,7 +6928,7 @@ var TypeAnalyzer = class {
|
|
|
6906
6928
|
if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
|
|
6907
6929
|
this.diagnostics.push({
|
|
6908
6930
|
node: stmt,
|
|
6909
|
-
message: `Type '${
|
|
6931
|
+
message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
|
|
6910
6932
|
});
|
|
6911
6933
|
} else if (declared.kind !== "any") {
|
|
6912
6934
|
this.reportExcessProperties(source, declared);
|
|
@@ -7000,7 +7022,7 @@ var TypeAnalyzer = class {
|
|
|
7000
7022
|
if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
|
|
7001
7023
|
this.diagnostics.push({
|
|
7002
7024
|
node: stmt,
|
|
7003
|
-
message: `Type '${
|
|
7025
|
+
message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
|
|
7004
7026
|
});
|
|
7005
7027
|
}
|
|
7006
7028
|
this.setBinding(env, id, narrowTo(declared, next));
|
|
@@ -7085,21 +7107,23 @@ var TypeAnalyzer = class {
|
|
|
7085
7107
|
const valueId = value.type === "IdentifierPattern" ? this.bindingIdByName(value.name, value) : void 0;
|
|
7086
7108
|
if (keyId !== void 0 && valueId !== void 0) this.correlateBindings(bodyEnv, [keyId, valueId], rows);
|
|
7087
7109
|
} else {
|
|
7088
|
-
const [keyT, valT] = this.iterationTypes(stmt.iterators[0], iterTypes[0], stmt.variables.length);
|
|
7110
|
+
const [keyT, valT, iteratesValues] = this.iterationTypes(stmt.iterators[0], iterTypes[0], stmt.variables.length);
|
|
7089
7111
|
stmt.variables.forEach((v, i) => {
|
|
7090
7112
|
this.bindPattern(v, i === 0 ? keyT : i === 1 ? valT : unknownType, bodyEnv, "widen");
|
|
7091
7113
|
});
|
|
7114
|
+
if (iteratesValues && stmt.iterators.length === 1) this.iteratesValues.add(stmt);
|
|
7092
7115
|
}
|
|
7093
7116
|
this.visitBlock(stmt.body, bodyEnv);
|
|
7094
7117
|
return;
|
|
7095
7118
|
}
|
|
7096
7119
|
case "ReturnStatement": {
|
|
7097
7120
|
const declared = this.declaredReturns[this.declaredReturns.length - 1];
|
|
7098
|
-
|
|
7121
|
+
const wanted = declared ?? this.contextualReturns[this.contextualReturns.length - 1];
|
|
7122
|
+
if (wanted) {
|
|
7099
7123
|
if (stmt.arguments.length === 1) {
|
|
7100
|
-
this.applyContext(stmt.arguments[0],
|
|
7101
|
-
} else if (
|
|
7102
|
-
stmt.arguments.forEach((a, i) => this.applyContext(a,
|
|
7124
|
+
this.applyContext(stmt.arguments[0], wanted);
|
|
7125
|
+
} else if (wanted.kind === "tuple" && wanted.isPack) {
|
|
7126
|
+
stmt.arguments.forEach((a, i) => this.applyContext(a, wanted.elements[i]));
|
|
7103
7127
|
}
|
|
7104
7128
|
}
|
|
7105
7129
|
const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
|
|
@@ -7328,6 +7352,17 @@ var TypeAnalyzer = class {
|
|
|
7328
7352
|
/** What a function expression's unannotated parameters are, from where
|
|
7329
7353
|
* it is written — see `applyContext`. */
|
|
7330
7354
|
contextualParams = /* @__PURE__ */ new WeakMap();
|
|
7355
|
+
/** What a function expression written in a typed position returns, from
|
|
7356
|
+
* that position — see `applyContext`. A body with no `return` annotation
|
|
7357
|
+
* would otherwise infer its `return` expressions with nothing wanted of
|
|
7358
|
+
* them, and widen the literals in them: `() => S = function() { return
|
|
7359
|
+
* { Status: true } }` inferred `Status: boolean` and then failed against
|
|
7360
|
+
* `S`. It is a hint, not a contract: only `applyContext` reads it, so a
|
|
7361
|
+
* body that does not match still reports at the assignment, not here. */
|
|
7362
|
+
contextualReturnOf = /* @__PURE__ */ new WeakMap();
|
|
7363
|
+
/** `contextualReturnOf` for each function body being walked, alongside
|
|
7364
|
+
* `declaredReturns`. */
|
|
7365
|
+
contextualReturns = [];
|
|
7331
7366
|
/** `expected` is the type the surroundings want for `expr`. A function
|
|
7332
7367
|
* expression written there takes its unannotated parameters' types from
|
|
7333
7368
|
* it, as in TypeScript: `signal:Connect(function(player) ... end)` knows
|
|
@@ -7350,6 +7385,10 @@ var TypeAnalyzer = class {
|
|
|
7350
7385
|
const members = expected.kind === "union" ? expected.types : [expected];
|
|
7351
7386
|
const signatures = members.flatMap((m) => this.overloadsOf(this.expand(m)));
|
|
7352
7387
|
if (!signatures.length) return;
|
|
7388
|
+
if (!e.func.returnType && !e.func.predicate) {
|
|
7389
|
+
const returns = signatures.map((s) => s.returns).filter((t) => !containsTypeParam(t));
|
|
7390
|
+
if (returns.length) this.contextualReturnOf.set(e.func, union(returns));
|
|
7391
|
+
}
|
|
7353
7392
|
e.func.params.forEach((p, k) => {
|
|
7354
7393
|
if (p.typeAnnotation || p.pattern || p.default) return;
|
|
7355
7394
|
const candidates = [];
|
|
@@ -7393,7 +7432,9 @@ var TypeAnalyzer = class {
|
|
|
7393
7432
|
return property ? [property.type] : o.indexer ? [o.indexer.value] : [];
|
|
7394
7433
|
});
|
|
7395
7434
|
if (types.length) {
|
|
7396
|
-
|
|
7435
|
+
const target = field.type === "TableFieldShorthand" ? field.name : field.value;
|
|
7436
|
+
this.contextArms.set(target, types);
|
|
7437
|
+
this.applyContext(target, union(types));
|
|
7397
7438
|
}
|
|
7398
7439
|
}
|
|
7399
7440
|
}
|
|
@@ -7454,11 +7495,13 @@ var TypeAnalyzer = class {
|
|
|
7454
7495
|
withVarargs(func, body) {
|
|
7455
7496
|
this.varargs.push(this.varargElement(func));
|
|
7456
7497
|
this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
|
|
7498
|
+
this.contextualReturns.push(this.contextualReturnOf.get(func));
|
|
7457
7499
|
try {
|
|
7458
7500
|
return body();
|
|
7459
7501
|
} finally {
|
|
7460
7502
|
this.varargs.pop();
|
|
7461
7503
|
this.declaredReturns.pop();
|
|
7504
|
+
this.contextualReturns.pop();
|
|
7462
7505
|
}
|
|
7463
7506
|
}
|
|
7464
7507
|
visitFunctionBodyInner(func, outerEnv) {
|
|
@@ -8100,22 +8143,26 @@ var TypeAnalyzer = class {
|
|
|
8100
8143
|
if (ids.length < 2) return;
|
|
8101
8144
|
this.correlateBindings(env, ids, objects.map((member) => names.map((name) => this.propertyType(member, name))));
|
|
8102
8145
|
}
|
|
8103
|
-
/** `(keyType, valueType)` yielded by a generic-for
|
|
8104
|
-
* `ipairs`/`pairs`/`next(t)` and Luau generalized
|
|
8105
|
-
* `varCount` is how many loop variables were
|
|
8146
|
+
/** `(keyType, valueType, iteratesValues)` yielded by a generic-for
|
|
8147
|
+
* iterator. Handles `ipairs`/`pairs`/`next(t)` and Luau generalized
|
|
8148
|
+
* iteration (`for … in t`). `varCount` is how many loop variables were
|
|
8149
|
+
* written. `iteratesValues` reports the one case where the first variable
|
|
8150
|
+
* is bound to the value rather than the key, which is what lowering has
|
|
8151
|
+
* to know to emit the loop; see `TypeAnalysis.iteratesValues`. */
|
|
8106
8152
|
iterationTypes(iterNode, iterType, varCount) {
|
|
8107
8153
|
if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
|
|
8108
8154
|
const name = iterNode.callee.name;
|
|
8109
8155
|
const src = this.expand(this.typeOf.get(iterNode.arguments[0]) ?? unknownType);
|
|
8110
|
-
if (name === "ipairs") return [numberType, this.elementType(src, 0)];
|
|
8156
|
+
if (name === "ipairs") return [numberType, this.elementType(src, 0), false];
|
|
8111
8157
|
if (name === "pairs" || name === "next") {
|
|
8112
8158
|
if (src.kind === "object") {
|
|
8113
8159
|
return [
|
|
8114
8160
|
src.indexer?.key ?? stringType,
|
|
8115
|
-
src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
|
|
8161
|
+
src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type)),
|
|
8162
|
+
false
|
|
8116
8163
|
];
|
|
8117
8164
|
}
|
|
8118
|
-
if (src.kind === "array") return [numberType, src.element];
|
|
8165
|
+
if (src.kind === "array") return [numberType, src.element, false];
|
|
8119
8166
|
}
|
|
8120
8167
|
}
|
|
8121
8168
|
const iterator = this.expand(iterType);
|
|
@@ -8123,16 +8170,18 @@ var TypeAnalyzer = class {
|
|
|
8123
8170
|
const returns = this.expand(iterator.returns);
|
|
8124
8171
|
const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
|
|
8125
8172
|
const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
|
|
8126
|
-
return [at(0), at(1)];
|
|
8173
|
+
return [at(0), at(1), false];
|
|
8127
8174
|
}
|
|
8128
8175
|
const t = this.expand(iterType);
|
|
8129
|
-
if (t.kind === "array")
|
|
8176
|
+
if (t.kind === "array") {
|
|
8177
|
+
return varCount >= 2 ? [numberType, t.element, false] : [t.element, unknownType, true];
|
|
8178
|
+
}
|
|
8130
8179
|
if (t.kind === "object") {
|
|
8131
8180
|
const k = t.indexer?.key ?? stringType;
|
|
8132
8181
|
const v = t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type));
|
|
8133
|
-
return varCount >= 2 ? [k, v] : [v, unknownType];
|
|
8182
|
+
return varCount >= 2 ? [k, v, false] : [v, unknownType, true];
|
|
8134
8183
|
}
|
|
8135
|
-
return [unknownType, unknownType];
|
|
8184
|
+
return [unknownType, unknownType, false];
|
|
8136
8185
|
}
|
|
8137
8186
|
inferReturnType(body, env) {
|
|
8138
8187
|
const returns = [];
|
|
@@ -8275,6 +8324,7 @@ var TypeAnalyzer = class {
|
|
|
8275
8324
|
* resolving, which is what breaks re-entry on a recursive alias. */
|
|
8276
8325
|
expand(t) {
|
|
8277
8326
|
if (t.kind !== "genericRef") return t;
|
|
8327
|
+
if (t.origin && t.origin !== this.origin) return t.origin.expand(t);
|
|
8278
8328
|
const def = this.aliasDefs.get(t.name);
|
|
8279
8329
|
if (!def) {
|
|
8280
8330
|
const imported = this.importedTypes.get(t.name);
|
|
@@ -8578,7 +8628,7 @@ var TypeAnalyzer = class {
|
|
|
8578
8628
|
if (!isAssignable(narrow, declared) && !isAssignable(actual, declared)) {
|
|
8579
8629
|
this.diagnostics.push({
|
|
8580
8630
|
node: expr,
|
|
8581
|
-
message: `Type '${
|
|
8631
|
+
message: `Type '${briefType(actual)}' does not satisfy the expected type '${briefType(declared)}'` + this.explainMismatch(actual, declared)
|
|
8582
8632
|
});
|
|
8583
8633
|
} else {
|
|
8584
8634
|
this.reportExcessProperties(expr.expression, declared);
|
|
@@ -8957,8 +9007,16 @@ var TypeAnalyzer = class {
|
|
|
8957
9007
|
* inferred, so this is a lookup rather than a second pass. */
|
|
8958
9008
|
widenUnlessAsked(value, at) {
|
|
8959
9009
|
const wanted = this.expectedTypeOf.get(at);
|
|
9010
|
+
if (value.kind === "literal") {
|
|
9011
|
+
const arms = this.contextArms.get(at);
|
|
9012
|
+
if (arms?.some((arm) => this.admitsLiteral(arm, value.base))) return value;
|
|
9013
|
+
}
|
|
8960
9014
|
return wanted === void 0 ? widen(value) : this.keepContextualLiterals(value, wanted);
|
|
8961
9015
|
}
|
|
9016
|
+
/** What each arm of the contract wanted of an expression, unmerged — see
|
|
9017
|
+
* `widenUnlessAsked`. Recorded by `applyTableContext` beside the merged
|
|
9018
|
+
* type it hands to `applyContext`. */
|
|
9019
|
+
contextArms = /* @__PURE__ */ new WeakMap();
|
|
8962
9020
|
inferObject(expr, env, asConst) {
|
|
8963
9021
|
const entries = [];
|
|
8964
9022
|
let indexer;
|
|
@@ -9667,8 +9725,9 @@ var TypeAnalyzer = class {
|
|
|
9667
9725
|
* themselves are then the whole story. */
|
|
9668
9726
|
explainMismatch(actual, expected) {
|
|
9669
9727
|
const from = this.expand(actual);
|
|
9670
|
-
|
|
9671
|
-
|
|
9728
|
+
if (from.kind !== "object") return "";
|
|
9729
|
+
const to = this.likeliestArm(from, this.expand(expected));
|
|
9730
|
+
if (to === void 0) return "";
|
|
9672
9731
|
const missing = [];
|
|
9673
9732
|
for (const [name, property] of to.properties) {
|
|
9674
9733
|
if (property.optional || from.properties.has(name) || INTERNAL_MEMBERS.has(name)) continue;
|
|
@@ -9693,6 +9752,34 @@ var TypeAnalyzer = class {
|
|
|
9693
9752
|
}
|
|
9694
9753
|
return "";
|
|
9695
9754
|
}
|
|
9755
|
+
/** Which object of a contract the value was reaching for. One object is
|
|
9756
|
+
* itself; among several, a discriminant decides — the arm whose literal
|
|
9757
|
+
* properties the value already agrees with (`Status: true` picks
|
|
9758
|
+
* `StatSuccess` over `StatError`). Failing that, the arm it is closest to
|
|
9759
|
+
* fitting, so the explanation is about the shortest gap rather than an
|
|
9760
|
+
* arbitrary one. `undefined` when there is no object to explain against. */
|
|
9761
|
+
likeliestArm(from, expected) {
|
|
9762
|
+
const arms = this.membersOf(expected).filter((m) => m.kind === "object");
|
|
9763
|
+
if (arms.length <= 1) return arms[0];
|
|
9764
|
+
const gapOf = (arm) => {
|
|
9765
|
+
let gap = 0;
|
|
9766
|
+
for (const [name, property] of arm.properties) {
|
|
9767
|
+
if (INTERNAL_MEMBERS.has(name)) continue;
|
|
9768
|
+
const own = from.properties.get(name);
|
|
9769
|
+
if (!own) {
|
|
9770
|
+
if (!property.optional) gap += 1;
|
|
9771
|
+
continue;
|
|
9772
|
+
}
|
|
9773
|
+
if (isAssignable(own.type, property.type)) continue;
|
|
9774
|
+
gap += this.expand(property.type).kind === "literal" ? 100 : 1;
|
|
9775
|
+
}
|
|
9776
|
+
for (const name of from.properties.keys()) {
|
|
9777
|
+
if (!INTERNAL_MEMBERS.has(name) && !arm.properties.has(name) && !arm.indexer) gap += 1;
|
|
9778
|
+
}
|
|
9779
|
+
return gap;
|
|
9780
|
+
};
|
|
9781
|
+
return arms.reduce((best, arm) => gapOf(arm) < gapOf(best) ? arm : best);
|
|
9782
|
+
}
|
|
9696
9783
|
bindingIdOf(id) {
|
|
9697
9784
|
return this.scopes.bindingOf.get(id);
|
|
9698
9785
|
}
|
|
@@ -9739,7 +9826,15 @@ function briefType(t) {
|
|
|
9739
9826
|
const shown = t.types.slice(0, 6).map(formatType).join(" | ");
|
|
9740
9827
|
return `${shown} | ... ${t.types.length - 6} more`;
|
|
9741
9828
|
}
|
|
9742
|
-
|
|
9829
|
+
const full = formatType(t);
|
|
9830
|
+
if (full.length <= MAX_TYPE) return full;
|
|
9831
|
+
if (t.kind === "object" && t.properties.size) {
|
|
9832
|
+
const names = [...t.properties.keys()].filter((n) => !INTERNAL_MEMBERS.has(n));
|
|
9833
|
+
const shown = names.slice(0, MAX_MISSING);
|
|
9834
|
+
const rest = names.length - shown.length;
|
|
9835
|
+
return `{ ${shown.join(", ")}${rest > 0 ? `, ... ${rest} more` : ""} }`;
|
|
9836
|
+
}
|
|
9837
|
+
return `${full.slice(0, MAX_TYPE)}...`;
|
|
9743
9838
|
}
|
|
9744
9839
|
|
|
9745
9840
|
// src/project/host.ts
|