Fix contextually typed object literal completions where the object being edited affects its own inference (#36556)

* Conditionally elide a parameter from contextual type signature calculation

* Slightly different approach to forbid inference to specific expressions

* Handle nested literals and mapped types correctly

* Delete unused cache

* Rename ContextFlags.BaseConstraint and related usage

* Add tests from my PR

* Update ContextFlags comment

Co-Authored-By: Wesley Wigham <wwigham@gmail.com>

* Update comments and fourslash triple slash refs

Co-authored-by: Wesley Wigham <wwigham@gmail.com>
This commit is contained in:
Andrew Branch
2020-01-31 15:37:18 -08:00
committed by GitHub
parent ad249043da
commit ef8eb0c876
8 changed files with 253 additions and 25 deletions
+11 -11
View File
@@ -1280,8 +1280,8 @@ namespace ts.Completions {
// Cursor is inside a JSX self-closing element or opening element
const attrsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes);
if (!attrsType) return GlobalsSearch.Continue;
const baseType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes, ContextFlags.BaseConstraint);
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, baseType, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
const completionsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes, ContextFlags.Completions);
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, completionsType, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
setSortTextToOptionalMember();
completionKind = CompletionKind.MemberLike;
isNewIdentifierLocation = false;
@@ -1800,10 +1800,10 @@ namespace ts.Completions {
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
const instantiatedType = typeChecker.getContextualType(objectLikeContainer);
const baseType = instantiatedType && typeChecker.getContextualType(objectLikeContainer, ContextFlags.BaseConstraint);
if (!instantiatedType || !baseType) return GlobalsSearch.Fail;
isNewIdentifierLocation = hasIndexSignature(instantiatedType || baseType);
typeMembers = getPropertiesForObjectExpression(instantiatedType, baseType, objectLikeContainer, typeChecker);
const completionsType = instantiatedType && typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions);
if (!instantiatedType || !completionsType) return GlobalsSearch.Fail;
isNewIdentifierLocation = hasIndexSignature(instantiatedType || completionsType);
typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker);
existingMembers = objectLikeContainer.properties;
}
else {
@@ -2549,10 +2549,10 @@ namespace ts.Completions {
return jsdoc && jsdoc.tags && (rangeContainsPosition(jsdoc, position) ? findLast(jsdoc.tags, tag => tag.pos < position) : undefined);
}
function getPropertiesForObjectExpression(contextualType: Type, baseConstrainedType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] {
const hasBaseType = baseConstrainedType && baseConstrainedType !== contextualType;
const type = hasBaseType && !(baseConstrainedType!.flags & TypeFlags.AnyOrUnknown)
? checker.getUnionType([contextualType, baseConstrainedType!])
function getPropertiesForObjectExpression(contextualType: Type, completionsType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] {
const hasCompletionsType = completionsType && completionsType !== contextualType;
const type = hasCompletionsType && !(completionsType!.flags & TypeFlags.AnyOrUnknown)
? checker.getUnionType([contextualType, completionsType!])
: contextualType;
const properties = type.isUnion()
@@ -2564,7 +2564,7 @@ namespace ts.Completions {
checker.isTypeInvalidDueToUnionDiscriminant(memberType, obj))))
: type.getApparentProperties();
return hasBaseType ? properties.filter(hasDeclarationOtherThanSelf) : properties;
return hasCompletionsType ? properties.filter(hasDeclarationOtherThanSelf) : properties;
// Filter out members whose only declaration is the object literal itself to avoid
// self-fulfilling completions like: