Skip to content

Commit bed7c89

Browse files
authored
Add base constraint completions for JSX attributes (#35803)
* Add base constraint completions for JSX attributes * Add test for class components
1 parent d66e959 commit bed7c89

File tree

3 files changed

+48
-11
lines changed

3 files changed

+48
-11
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21213,14 +21213,14 @@ namespace ts {
2121321213
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number, contextFlags?: ContextFlags): Type {
2121421214
// If we're already in the process of resolving the given signature, don't resolve again as
2121521215
// that could cause infinite recursion. Instead, return anySignature.
21216-
const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
21216+
let signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
21217+
if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature.target && !hasTypeArguments(callTarget)) {
21218+
signature = getBaseSignature(signature.target);
21219+
}
21220+
2121721221
if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
2121821222
return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
2121921223
}
21220-
if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature.target && !hasTypeArguments(callTarget)) {
21221-
const baseSignature = getBaseSignature(signature.target);
21222-
return getTypeAtPosition(baseSignature, argIndex);
21223-
}
2122421224
return getTypeAtPosition(signature, argIndex);
2122521225
}
2122621226

@@ -21645,7 +21645,7 @@ namespace ts {
2164521645
return getContextualTypeForJsxAttribute(<JsxAttribute | JsxSpreadAttribute>parent);
2164621646
case SyntaxKind.JsxOpeningElement:
2164721647
case SyntaxKind.JsxSelfClosingElement:
21648-
return getContextualJsxElementAttributesType(<JsxOpeningLikeElement>parent);
21648+
return getContextualJsxElementAttributesType(<JsxOpeningLikeElement>parent, contextFlags);
2164921649
}
2165021650
return undefined;
2165121651
}
@@ -21655,18 +21655,20 @@ namespace ts {
2165521655
return ancestor && ancestor.inferenceContext!;
2165621656
}
2165721657

21658-
function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement) {
21659-
if (isJsxOpeningElement(node) && node.parent.contextualType) {
21658+
function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags?: ContextFlags) {
21659+
if (isJsxOpeningElement(node) && node.parent.contextualType && contextFlags !== ContextFlags.BaseConstraint) {
2166021660
// Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit
2166121661
// _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type
2166221662
// (as below) instead!
2166321663
return node.parent.contextualType;
2166421664
}
21665-
return getContextualTypeForArgumentAtIndex(node, 0);
21665+
return getContextualTypeForArgumentAtIndex(node, 0, contextFlags);
2166621666
}
2166721667

2166821668
function getEffectiveFirstArgumentForJsxSignature(signature: Signature, node: JsxOpeningLikeElement) {
21669-
return getJsxReferenceKind(node) !== JsxReferenceKind.Component ? getJsxPropsTypeFromCallSignature(signature, node) : getJsxPropsTypeFromClassType(signature, node);
21669+
return getJsxReferenceKind(node) !== JsxReferenceKind.Component
21670+
? getJsxPropsTypeFromCallSignature(signature, node)
21671+
: getJsxPropsTypeFromClassType(signature, node);
2167021672
}
2167121673

2167221674
function getJsxPropsTypeFromCallSignature(sig: Signature, context: JsxOpeningLikeElement) {

src/services/completions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,8 @@ namespace ts.Completions {
12791279
// Cursor is inside a JSX self-closing element or opening element
12801280
const attrsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes);
12811281
if (!attrsType) return GlobalsSearch.Continue;
1282-
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, /*baseType*/ undefined, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
1282+
const baseType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes, ContextFlags.BaseConstraint);
1283+
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, baseType, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
12831284
setSortTextToOptionalMember();
12841285
completionKind = CompletionKind.MemberLike;
12851286
isNewIdentifierLocation = false;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @jsx: preserve
4+
5+
// @Filename: index.tsx
6+
////declare namespace JSX {
7+
//// interface Element {}
8+
//// interface IntrinsicElements {}
9+
//// interface ElementAttributesProperty { props }
10+
////}
11+
////
12+
////type Props = { a?: string, b?: string };
13+
////function Component<T extends Props>(props: T) { return null; }
14+
////const e1 = <Component /*1*/ />;
15+
////const e2 = <Component /*2*/></Component>
16+
////
17+
////declare class Component2<T extends Props> {
18+
//// props: T;
19+
////}
20+
////const e3 = <Component2 /*3*/ />;
21+
////const e4 = <Component2 /*4*/></Component2>;
22+
23+
["1", "2", "3", "4"].forEach(marker => {
24+
verify.completions({
25+
marker,
26+
exact: [{
27+
name: "a",
28+
sortText: completion.SortText.OptionalMember
29+
}, {
30+
name: "b",
31+
sortText: completion.SortText.OptionalMember
32+
}]
33+
});
34+
});

0 commit comments

Comments
 (0)