diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4095b0b4b5c7a..84df6d7215b30 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21190,14 +21190,14 @@ namespace ts { function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number, contextFlags?: ContextFlags): Type { // If we're already in the process of resolving the given signature, don't resolve again as // that could cause infinite recursion. Instead, return anySignature. - const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget); + let signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget); + if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature.target && !hasTypeArguments(callTarget)) { + signature = getBaseSignature(signature.target); + } + if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) { return getEffectiveFirstArgumentForJsxSignature(signature, callTarget); } - if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature.target && !hasTypeArguments(callTarget)) { - const baseSignature = getBaseSignature(signature.target); - return getTypeAtPosition(baseSignature, argIndex); - } return getTypeAtPosition(signature, argIndex); } @@ -21622,7 +21622,7 @@ namespace ts { return getContextualTypeForJsxAttribute(parent); case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxSelfClosingElement: - return getContextualJsxElementAttributesType(parent); + return getContextualJsxElementAttributesType(parent, contextFlags); } return undefined; } @@ -21632,18 +21632,20 @@ namespace ts { return ancestor && ancestor.inferenceContext!; } - function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement) { - if (isJsxOpeningElement(node) && node.parent.contextualType) { + function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags?: ContextFlags) { + if (isJsxOpeningElement(node) && node.parent.contextualType && contextFlags !== ContextFlags.BaseConstraint) { // Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit // _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type // (as below) instead! return node.parent.contextualType; } - return getContextualTypeForArgumentAtIndex(node, 0); + return getContextualTypeForArgumentAtIndex(node, 0, contextFlags); } function getEffectiveFirstArgumentForJsxSignature(signature: Signature, node: JsxOpeningLikeElement) { - return getJsxReferenceKind(node) !== JsxReferenceKind.Component ? getJsxPropsTypeFromCallSignature(signature, node) : getJsxPropsTypeFromClassType(signature, node); + return getJsxReferenceKind(node) !== JsxReferenceKind.Component + ? getJsxPropsTypeFromCallSignature(signature, node) + : getJsxPropsTypeFromClassType(signature, node); } function getJsxPropsTypeFromCallSignature(sig: Signature, context: JsxOpeningLikeElement) { diff --git a/src/services/completions.ts b/src/services/completions.ts index af3698d5cd1d7..817feb324b326 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1279,7 +1279,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; - symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, /*baseType*/ undefined, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties); + const baseType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes, ContextFlags.BaseConstraint); + symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, baseType, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties); setSortTextToOptionalMember(); completionKind = CompletionKind.MemberLike; isNewIdentifierLocation = false; diff --git a/tests/cases/fourslash/completionsJsxAttributeGeneric2.ts b/tests/cases/fourslash/completionsJsxAttributeGeneric2.ts new file mode 100644 index 0000000000000..5aafeaa9e7cd5 --- /dev/null +++ b/tests/cases/fourslash/completionsJsxAttributeGeneric2.ts @@ -0,0 +1,34 @@ +/// + +// @jsx: preserve + +// @Filename: index.tsx +////declare namespace JSX { +//// interface Element {} +//// interface IntrinsicElements {} +//// interface ElementAttributesProperty { props } +////} +//// +////type Props = { a?: string, b?: string }; +////function Component(props: T) { return null; } +////const e1 = ; +////const e2 = +//// +////declare class Component2 { +//// props: T; +////} +////const e3 = ; +////const e4 = ; + +["1", "2", "3", "4"].forEach(marker => { + verify.completions({ + marker, + exact: [{ + name: "a", + sortText: completion.SortText.OptionalMember + }, { + name: "b", + sortText: completion.SortText.OptionalMember + }] + }); +});