Skip to content

Commit 945d423

Browse files
committed
Fix bugs in combineUsages/getSignatureFromCalls
1 parent c93f919 commit 945d423

File tree

1 file changed

+57
-21
lines changed

1 file changed

+57
-21
lines changed

src/services/codefixes/inferFromUsage.ts

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -405,18 +405,19 @@ namespace ts.codefix {
405405
}
406406

407407
interface Usage {
408-
isNumber?: boolean;
409-
isString?: boolean;
408+
isNumber: boolean | undefined;
409+
isString: boolean | undefined;
410410
/** Used ambiguously, eg x + ___ or object[___]; results in string | number if no other evidence exists */
411-
isNumberOrString?: boolean;
412-
413-
candidateTypes?: Type[];
414-
properties?: UnderscoreEscapedMap<Usage>;
415-
calls?: CallUsage[];
416-
constructs?: CallUsage[];
417-
numberIndex?: Usage;
418-
stringIndex?: Usage;
419-
candidateThisTypes?: Type[];
411+
isNumberOrString: boolean | undefined;
412+
413+
candidateTypes: Type[] | undefined;
414+
properties: UnderscoreEscapedMap<Usage> | undefined;
415+
calls: CallUsage[] | undefined;
416+
constructs: CallUsage[] | undefined;
417+
numberIndex: Usage | undefined;
418+
stringIndex: Usage | undefined;
419+
candidateThisTypes: Type[] | undefined;
420+
inferredTypes: Type[] | undefined;
420421
}
421422

422423
function single(): Type {
@@ -428,7 +429,7 @@ namespace ts.codefix {
428429
return undefined;
429430
}
430431

431-
const usage: Usage = {};
432+
const usage = createEmptyUsage();
432433
for (const reference of references) {
433434
cancellationToken.throwIfCancellationRequested();
434435
calculateUsageOfNode(reference, usage);
@@ -466,7 +467,7 @@ namespace ts.codefix {
466467
}
467468

468469
function thisParameter() {
469-
const usage: Usage = {};
470+
const usage = createEmptyUsage();
470471
for (const reference of references) {
471472
cancellationToken.throwIfCancellationRequested();
472473
calculateUsageOfNode(reference, usage);
@@ -476,7 +477,7 @@ namespace ts.codefix {
476477
}
477478

478479
function inferTypesFromReferencesSingle(references: readonly Identifier[]): Type[] {
479-
const usage: Usage = {};
480+
const usage: Usage = createEmptyUsage();
480481
for (const reference of references) {
481482
cancellationToken.throwIfCancellationRequested();
482483
calculateUsageOfNode(reference, usage);
@@ -671,7 +672,7 @@ namespace ts.codefix {
671672
function inferTypeFromCallExpression(parent: CallExpression | NewExpression, usage: Usage): void {
672673
const call: CallUsage = {
673674
argumentTypes: [],
674-
return_: {}
675+
return_: createEmptyUsage()
675676
};
676677

677678
if (parent.arguments) {
@@ -695,7 +696,7 @@ namespace ts.codefix {
695696
if (!usage.properties) {
696697
usage.properties = createUnderscoreEscapedMap<Usage>();
697698
}
698-
const propertyUsage = usage.properties.get(name) || { };
699+
const propertyUsage = usage.properties.get(name) || createEmptyUsage();
699700
calculateUsageOfNode(parent, propertyUsage);
700701
usage.properties.set(name, propertyUsage);
701702
}
@@ -707,7 +708,7 @@ namespace ts.codefix {
707708
}
708709
else {
709710
const indexType = checker.getTypeAtLocation(parent.argumentExpression);
710-
const indexUsage = {};
711+
const indexUsage = createEmptyUsage();
711712
calculateUsageOfNode(parent, indexUsage);
712713
if (indexType.flags & TypeFlags.NumberLike) {
713714
usage.numberIndex = indexUsage;
@@ -845,7 +846,10 @@ namespace ts.codefix {
845846
if (usage.numberIndex) {
846847
types.push(checker.createArrayType(recur(usage.numberIndex)));
847848
}
848-
else if (usage.properties || usage.calls || usage.constructs || usage.stringIndex) {
849+
else if (usage.properties && usage.properties.size
850+
|| usage.calls && usage.calls.length
851+
|| usage.constructs && usage.constructs.length
852+
|| usage.stringIndex) {
849853
const members = createUnderscoreEscapedMap<Symbol>();
850854
const callSignatures: Signature[] = [];
851855
const constructSignatures: Signature[] = [];
@@ -873,25 +877,57 @@ namespace ts.codefix {
873877

874878
types.push(checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined)); // TODO: GH#18217
875879
}
876-
return types;
880+
return types; // TODO: Should cache this since I HOPE it doesn't change
877881

878882
function recur(innerUsage: Usage): Type {
879883
return unifyFromUsage(inferFromUsage(innerUsage));
880884
}
881885
}
882886

887+
function createEmptyUsage(): Usage {
888+
return {
889+
isNumber: undefined,
890+
isString: undefined,
891+
isNumberOrString: undefined,
892+
candidateTypes: undefined,
893+
properties: undefined,
894+
calls: undefined,
895+
constructs: undefined,
896+
numberIndex: undefined,
897+
stringIndex: undefined,
898+
candidateThisTypes: undefined,
899+
inferredTypes: undefined,
900+
}
901+
}
902+
883903
function combineUsages(usages: Usage[]): Usage {
904+
const combinedProperties = createUnderscoreEscapedMap<Usage[]>()
905+
for (const u of usages) {
906+
if (u.properties) {
907+
u.properties.forEach((p,name) => {
908+
if (!combinedProperties.has(name)) {
909+
combinedProperties.set(name, []);
910+
}
911+
combinedProperties.get(name)!.push(p);
912+
});
913+
}
914+
}
915+
const properties = createUnderscoreEscapedMap<Usage>()
916+
combinedProperties.forEach((ps,name) => {
917+
properties.set(name, combineUsages(ps));
918+
});
884919
return {
885920
isNumber: usages.some(u => u.isNumber),
886921
isString: usages.some(u => u.isString),
887922
isNumberOrString: usages.some(u => u.isNumberOrString),
888923
candidateTypes: flatMap(usages, u => u.candidateTypes) as Type[],
889-
properties: undefined, // TODO
924+
properties,
890925
calls: flatMap(usages, u => u.calls) as CallUsage[],
891926
constructs: flatMap(usages, u => u.constructs) as CallUsage[],
892927
numberIndex: forEach(usages, u => u.numberIndex),
893928
stringIndex: forEach(usages, u => u.stringIndex),
894929
candidateThisTypes: flatMap(usages, u => u.candidateThisTypes) as Type[],
930+
inferredTypes: undefined, // clear type cache
895931
}
896932
}
897933

@@ -901,7 +937,7 @@ namespace ts.codefix {
901937
checker.getNumberType(),
902938
checker.createArrayType(checker.getAnyType()),
903939
checker.createPromiseType(checker.getAnyType()),
904-
// checker.getFunctionType() // not sure what this was supposed to be good for.
940+
// checker.getFunctionType() // TODO: not sure what this was supposed to be good for.
905941
];
906942
const matches = builtins.filter(t => matchesAllPropertiesOf(t, usage));
907943
if (false && 0 < matches.length && matches.length < 3) {

0 commit comments

Comments
 (0)