Skip to content

Commit f394190

Browse files
committed
Tonnes of cleanup
1 parent d32c6b2 commit f394190

File tree

1 file changed

+87
-80
lines changed

1 file changed

+87
-80
lines changed

src/services/codefixes/inferFromUsage.ts

Lines changed: 87 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,53 @@ namespace ts.codefix {
433433
inferredTypes: Type[] | undefined;
434434
}
435435

436+
function createEmptyUsage(): Usage {
437+
return {
438+
isNumber: undefined,
439+
isString: undefined,
440+
isNumberOrString: undefined,
441+
candidateTypes: undefined,
442+
properties: undefined,
443+
calls: undefined,
444+
constructs: undefined,
445+
numberIndex: undefined,
446+
stringIndex: undefined,
447+
candidateThisTypes: undefined,
448+
inferredTypes: undefined,
449+
};
450+
}
451+
452+
function combineUsages(usages: Usage[]): Usage {
453+
const combinedProperties = createUnderscoreEscapedMap<Usage[]>();
454+
for (const u of usages) {
455+
if (u.properties) {
456+
u.properties.forEach((p, name) => {
457+
if (!combinedProperties.has(name)) {
458+
combinedProperties.set(name, []);
459+
}
460+
combinedProperties.get(name)!.push(p);
461+
});
462+
}
463+
}
464+
const properties = createUnderscoreEscapedMap<Usage>();
465+
combinedProperties.forEach((ps, name) => {
466+
properties.set(name, combineUsages(ps));
467+
});
468+
return {
469+
isNumber: usages.some(u => u.isNumber),
470+
isString: usages.some(u => u.isString),
471+
isNumberOrString: usages.some(u => u.isNumberOrString),
472+
candidateTypes: flatMap(usages, u => u.candidateTypes) as Type[],
473+
properties,
474+
calls: flatMap(usages, u => u.calls) as CallUsage[],
475+
constructs: flatMap(usages, u => u.constructs) as CallUsage[],
476+
numberIndex: forEach(usages, u => u.numberIndex),
477+
stringIndex: forEach(usages, u => u.stringIndex),
478+
candidateThisTypes: flatMap(usages, u => u.candidateThisTypes) as Type[],
479+
inferredTypes: undefined, // clear type cache
480+
};
481+
}
482+
436483
function single(): Type {
437484
return unifyTypes(inferTypesFromReferencesSingle(references));
438485
}
@@ -767,6 +814,10 @@ namespace ts.codefix {
767814
return inferences.filter(i => toRemove.every(f => !f(i)));
768815
}
769816

817+
function unifyFromUsage(usage: Usage) {
818+
return unifyTypes(inferFromUsage(usage));
819+
}
820+
770821
function unifyTypes(inferences: ReadonlyArray<Type>): Type {
771822
if (!inferences.length) return checker.getAnyType();
772823

@@ -837,7 +888,7 @@ namespace ts.codefix {
837888
numberIndices.length ? checker.createIndexInfo(checker.getUnionType(numberIndices), numberIndexReadonly) : undefined);
838889
}
839890

840-
function inferFromUsage(usage: Usage) {
891+
function inferFromUsage(usage: Usage): Type[] {
841892
const types = [];
842893

843894
if (usage.isNumber) {
@@ -851,12 +902,20 @@ namespace ts.codefix {
851902
}
852903

853904
types.push(...(usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t)));
854-
types.push(...findBuiltinTypes(usage));
905+
types.push(...inferNamedTypesFromProperties(usage));
855906

856907
if (usage.numberIndex) {
857-
types.push(checker.createArrayType(recur(usage.numberIndex)));
908+
types.push(checker.createArrayType(unifyFromUsage(usage.numberIndex)));
909+
}
910+
const structural = inferStructuralType(usage);
911+
if (structural) {
912+
types.push(structural);
858913
}
859-
else if (usage.properties && usage.properties.size
914+
return types;
915+
}
916+
917+
function inferStructuralType(usage: Usage) {
918+
if (usage.properties && usage.properties.size
860919
|| usage.calls && usage.calls.length
861920
|| usage.constructs && usage.constructs.length
862921
|| usage.stringIndex) {
@@ -868,7 +927,7 @@ namespace ts.codefix {
868927
if (usage.properties) {
869928
usage.properties.forEach((u, name) => {
870929
const symbol = checker.createSymbol(SymbolFlags.Property, name);
871-
symbol.type = recur(u);
930+
symbol.type = unifyFromUsage(u);
872931
members.set(name, symbol);
873932
});
874933
}
@@ -882,75 +941,23 @@ namespace ts.codefix {
882941
}
883942

884943
if (usage.stringIndex) {
885-
stringIndexInfo = checker.createIndexInfo(recur(usage.stringIndex), /*isReadonly*/ false);
944+
stringIndexInfo = checker.createIndexInfo(unifyFromUsage(usage.stringIndex), /*isReadonly*/ false);
886945
}
887946

888-
types.push(checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined)); // TODO: GH#18217
889-
}
890-
return types; // TODO: Should cache this since I HOPE it doesn't change
891-
892-
function recur(innerUsage: Usage): Type {
893-
return unifyTypes(inferFromUsage(innerUsage));
947+
return checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined); // TODO: GH#18217
894948
}
895949
}
896950

897-
function createEmptyUsage(): Usage {
898-
return {
899-
isNumber: undefined,
900-
isString: undefined,
901-
isNumberOrString: undefined,
902-
candidateTypes: undefined,
903-
properties: undefined,
904-
calls: undefined,
905-
constructs: undefined,
906-
numberIndex: undefined,
907-
stringIndex: undefined,
908-
candidateThisTypes: undefined,
909-
inferredTypes: undefined,
910-
}
911-
}
912-
913-
function combineUsages(usages: Usage[]): Usage {
914-
const combinedProperties = createUnderscoreEscapedMap<Usage[]>()
915-
for (const u of usages) {
916-
if (u.properties) {
917-
u.properties.forEach((p,name) => {
918-
if (!combinedProperties.has(name)) {
919-
combinedProperties.set(name, []);
920-
}
921-
combinedProperties.get(name)!.push(p);
922-
});
923-
}
924-
}
925-
const properties = createUnderscoreEscapedMap<Usage>()
926-
combinedProperties.forEach((ps,name) => {
927-
properties.set(name, combineUsages(ps));
928-
});
929-
return {
930-
isNumber: usages.some(u => u.isNumber),
931-
isString: usages.some(u => u.isString),
932-
isNumberOrString: usages.some(u => u.isNumberOrString),
933-
candidateTypes: flatMap(usages, u => u.candidateTypes) as Type[],
934-
properties,
935-
calls: flatMap(usages, u => u.calls) as CallUsage[],
936-
constructs: flatMap(usages, u => u.constructs) as CallUsage[],
937-
numberIndex: forEach(usages, u => u.numberIndex),
938-
stringIndex: forEach(usages, u => u.stringIndex),
939-
candidateThisTypes: flatMap(usages, u => u.candidateThisTypes) as Type[],
940-
inferredTypes: undefined, // clear type cache
941-
}
942-
}
943-
944-
function findBuiltinTypes(usage: Usage): Type[] {
951+
function inferNamedTypesFromProperties(usage: Usage): Type[] {
945952
if (!usage.properties || !usage.properties.size) return [];
946-
const matches = builtins.filter(t => matchesAllPropertiesOf(t, usage));
953+
const matches = builtins.filter(t => allPropertiesAreAssignableToUsage(t, usage));
947954
if (0 < matches.length && matches.length < 3) {
948-
return matches.map(m => inferTypeParameterFromUsage(m, usage));
955+
return matches.map(m => inferInstantiationFromUsage(m, usage));
949956
}
950957
return [];
951958
}
952959

953-
function matchesAllPropertiesOf(type: Type, usage: Usage) {
960+
function allPropertiesAreAssignableToUsage(type: Type, usage: Usage) {
954961
if (!usage.properties) return false;
955962
let result = true;
956963
usage.properties.forEach((propUsage, name) => {
@@ -960,34 +967,34 @@ namespace ts.codefix {
960967
return;
961968
}
962969
if (propUsage.calls) {
963-
const sigs = checker.getSignaturesOfType(source, ts.SignatureKind.Call);
970+
const sigs = checker.getSignaturesOfType(source, SignatureKind.Call);
964971
result = result && !!sigs.length && checker.isTypeAssignableTo(source, getFunctionFromCalls(propUsage.calls));
965972
}
966973
else {
967-
result = result && checker.isTypeAssignableTo(source, unifyTypes(inferFromUsage(propUsage)));
974+
result = result && checker.isTypeAssignableTo(source, unifyFromUsage(propUsage));
968975
}
969976
});
970977
return result;
971978
}
972979

973-
// inference is limited to
974-
// 1. generic types with a single parameter
975-
// 2. inference to/from calls with a single signature
976-
function inferTypeParameterFromUsage(type: Type, usage: Usage) {
977-
if (!usage.properties || !(getObjectFlags(type) & ObjectFlags.Reference)) return type;
980+
/**
981+
* inference is limited to
982+
* 1. generic types with a single parameter
983+
* 2. inference to/from calls with a single signature
984+
*/
985+
function inferInstantiationFromUsage(type: Type, usage: Usage) {
986+
if (!(getObjectFlags(type) & ObjectFlags.Reference) || !usage.properties) {
987+
return type;
988+
}
978989
const generic = (type as TypeReference).target;
979990
const singleTypeParameter = singleOrUndefined(generic.typeParameters);
980991
if (!singleTypeParameter) return type;
981992

982993
const types: Type[] = [];
983994
usage.properties.forEach((propUsage, name) => {
984-
const source = checker.getTypeOfPropertyOfType(generic, name as string);
985-
if (!source) {
986-
return Debug.fail("generic should have all the properties of its reference.");
987-
}
988-
if (!propUsage.calls) return;
989-
990-
types.push(...infer(source, getFunctionFromCalls(propUsage.calls), singleTypeParameter));
995+
const genericPropertyType = checker.getTypeOfPropertyOfType(generic, name as string);
996+
Debug.assert(!!genericPropertyType, "generic should have all the properties of its reference.");
997+
types.push(...infer(genericPropertyType!, unifyFromUsage(propUsage), singleTypeParameter));
991998
});
992999
return builtinConstructors[type.symbol.escapedName as string](unifyTypes(types));
9931000
}
@@ -1033,7 +1040,7 @@ namespace ts.codefix {
10331040
break;
10341041
}
10351042
let sourceType = checker.getTypeOfSymbolAtLocation(sourceParam, sourceParam.valueDeclaration);
1036-
let elementType = isRest && checker.getElementTypeOfArrayType(sourceType);
1043+
const elementType = isRest && checker.getElementTypeOfArrayType(sourceType);
10371044
if (elementType) {
10381045
sourceType = elementType;
10391046
}
@@ -1047,7 +1054,7 @@ namespace ts.codefix {
10471054
}
10481055

10491056
function getFunctionFromCalls(calls: CallUsage[]) {
1050-
return checker.createAnonymousType(undefined!, createSymbolTable(), [getSignatureFromCalls(calls)], emptyArray, undefined, undefined);
1057+
return checker.createAnonymousType(undefined!, createSymbolTable(), [getSignatureFromCalls(calls)], emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
10511058
}
10521059

10531060
function getSignatureFromCalls(calls: CallUsage[]): Signature {
@@ -1061,7 +1068,7 @@ namespace ts.codefix {
10611068
}
10621069
parameters.push(symbol);
10631070
}
1064-
const returnType = unifyTypes(inferFromUsage(combineUsages(calls.map(call => call.return_))));
1071+
const returnType = unifyFromUsage(combineUsages(calls.map(call => call.return_)));
10651072
// TODO: GH#18217
10661073
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
10671074
}

0 commit comments

Comments
 (0)