Skip to content

Commit d9cf2be

Browse files
committed
Merge branch 'master' of github.com:Microsoft/TypeScript
2 parents a85f1d5 + e01c7d2 commit d9cf2be

12 files changed

+348
-199
lines changed

src/compiler/checker.ts

+70-45
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ namespace ts {
401401
const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
402402
const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
403403
const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
404+
const resolvingSignaturesArray = [resolvingSignature];
404405

405406
const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true);
406407
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
@@ -4024,6 +4025,10 @@ namespace ts {
40244025

40254026
function determineIfDeclarationIsVisible() {
40264027
switch (node.kind) {
4028+
case SyntaxKind.JSDocTypedefTag:
4029+
// Top-level jsdoc typedefs are considered exported
4030+
// First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file
4031+
return !!(node.parent && node.parent.parent && node.parent.parent.parent && isSourceFile(node.parent.parent.parent));
40274032
case SyntaxKind.BindingElement:
40284033
return isDeclarationVisible(node.parent.parent);
40294034
case SyntaxKind.VariableDeclaration:
@@ -5163,8 +5168,8 @@ namespace ts {
51635168
let result: TypeParameter[];
51645169
for (const node of symbol.declarations) {
51655170
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration ||
5166-
node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) {
5167-
const declaration = <InterfaceDeclaration | TypeAliasDeclaration>node;
5171+
node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration || node.kind === SyntaxKind.JSDocTypedefTag) {
5172+
const declaration = <InterfaceDeclaration | TypeAliasDeclaration | JSDocTypedefTag>node;
51685173
const typeParameters = getEffectiveTypeParameterDeclarations(declaration);
51695174
if (typeParameters) {
51705175
result = appendTypeParameters(result, typeParameters);
@@ -9046,7 +9051,7 @@ namespace ts {
90469051
}
90479052

90489053
function getAliasSymbolForTypeNode(node: TypeNode) {
9049-
return node.parent.kind === SyntaxKind.TypeAliasDeclaration ? getSymbolOfNode(node.parent) : undefined;
9054+
return (node.parent.kind === SyntaxKind.TypeAliasDeclaration || node.parent.kind === SyntaxKind.JSDocTypedefTag) ? getSymbolOfNode(node.parent) : undefined;
90509055
}
90519056

90529057
function getAliasTypeArgumentsForTypeNode(node: TypeNode) {
@@ -15355,8 +15360,17 @@ namespace ts {
1535515360
}
1535615361
}
1535715362

15358-
if (context.typeArguments) {
15359-
signatures = mapDefined(signatures, s => getJsxSignatureTypeArgumentInstantiation(s, context, isJs));
15363+
const links = getNodeLinks(context);
15364+
if (!links.resolvedSignatures) {
15365+
links.resolvedSignatures = createMap();
15366+
}
15367+
const cacheKey = "" + getTypeId(valueType);
15368+
if (links.resolvedSignatures.get(cacheKey) && links.resolvedSignatures.get(cacheKey) !== resolvingSignaturesArray) {
15369+
signatures = links.resolvedSignatures.get(cacheKey);
15370+
}
15371+
else if (!links.resolvedSignatures.get(cacheKey)) {
15372+
links.resolvedSignatures.set(cacheKey, resolvingSignaturesArray);
15373+
links.resolvedSignatures.set(cacheKey, signatures = instantiateJsxSignatures(context, signatures));
1536015374
}
1536115375

1536215376
return getUnionType(map(signatures, ctor ? t => getJsxPropsTypeFromClassType(t, isJs, context, /*reportErrors*/ false) : t => getJsxPropsTypeFromCallSignature(t, context)), UnionReduction.None);
@@ -16337,6 +16351,40 @@ namespace ts {
1633716351
return undefined;
1633816352
}
1633916353

16354+
function getInstantiatedJsxSignatures(openingLikeElement: JsxOpeningLikeElement, elementType: Type, reportErrors?: boolean) {
16355+
const links = getNodeLinks(openingLikeElement);
16356+
if (!links.resolvedSignatures) {
16357+
links.resolvedSignatures = createMap();
16358+
}
16359+
const cacheKey = "" + getTypeId(elementType);
16360+
if (links.resolvedSignatures.get(cacheKey) && links.resolvedSignatures.get(cacheKey) === resolvingSignaturesArray) {
16361+
return;
16362+
}
16363+
else if (links.resolvedSignatures.get(cacheKey)) {
16364+
return links.resolvedSignatures.get(cacheKey);
16365+
}
16366+
16367+
links.resolvedSignatures.set(cacheKey, resolvingSignaturesArray);
16368+
// Resolve the signatures, preferring constructor
16369+
let signatures = getSignaturesOfType(elementType, SignatureKind.Construct);
16370+
if (signatures.length === 0) {
16371+
// No construct signatures, try call signatures
16372+
signatures = getSignaturesOfType(elementType, SignatureKind.Call);
16373+
if (signatures.length === 0) {
16374+
// We found no signatures at all, which is an error
16375+
if (reportErrors) {
16376+
error(openingLikeElement.tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(openingLikeElement.tagName));
16377+
}
16378+
return;
16379+
}
16380+
}
16381+
16382+
// Instantiate in context of source type
16383+
const results = instantiateJsxSignatures(openingLikeElement, signatures);
16384+
links.resolvedSignatures.set(cacheKey, results);
16385+
return results;
16386+
}
16387+
1634016388
/**
1634116389
* Resolve attributes type of the given opening-like element. The attributes type is a type of attributes associated with the given elementType.
1634216390
* For instance:
@@ -16399,20 +16447,10 @@ namespace ts {
1639916447

1640016448
// Get the element instance type (the result of newing or invoking this tag)
1640116449

16402-
// Resolve the signatures, preferring constructor
16403-
let signatures = getSignaturesOfType(elementType, SignatureKind.Construct);
16404-
if (signatures.length === 0) {
16405-
// No construct signatures, try call signatures
16406-
signatures = getSignaturesOfType(elementType, SignatureKind.Call);
16407-
if (signatures.length === 0) {
16408-
// We found no signatures at all, which is an error
16409-
error(openingLikeElement.tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(openingLikeElement.tagName));
16410-
return unknownType;
16411-
}
16450+
const instantiatedSignatures = getInstantiatedJsxSignatures(openingLikeElement, elementType, /*reportErrors*/ true);
16451+
if (!length(instantiatedSignatures)) {
16452+
return unknownType;
1641216453
}
16413-
16414-
// Instantiate in context of source type
16415-
const instantiatedSignatures = instantiateJsxSignatures(openingLikeElement, signatures);
1641616454
const elemInstanceType = getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), UnionReduction.Subtype);
1641716455

1641816456
// If we should include all stateless attributes type, then get all attributes type from all stateless function signature.
@@ -18102,11 +18140,11 @@ namespace ts {
1810218140

1810318141
let typeArguments: NodeArray<TypeNode>;
1810418142

18105-
if (!isDecorator && !isJsxOpeningOrSelfClosingElement) {
18143+
if (!isDecorator) {
1810618144
typeArguments = (<CallExpression>node).typeArguments;
1810718145

1810818146
// We already perform checking on the type arguments on the class declaration itself.
18109-
if (isTaggedTemplate || (<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
18147+
if (isTaggedTemplate || isJsxOpeningOrSelfClosingElement || (<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
1811018148
forEach(typeArguments, checkSourceElement);
1811118149
}
1811218150
}
@@ -18695,30 +18733,6 @@ namespace ts {
1869518733
*/
1869618734
function getResolvedJsxStatelessFunctionSignature(openingLikeElement: JsxOpeningLikeElement, elementType: Type, candidatesOutArray: Signature[]): Signature | undefined {
1869718735
Debug.assert(!(elementType.flags & TypeFlags.Union));
18698-
return resolveStatelessJsxOpeningLikeElement(openingLikeElement, elementType, candidatesOutArray);
18699-
}
18700-
18701-
/**
18702-
* Try treating a given opening-like element as stateless function component and resolve a tagName to a function signature.
18703-
* @param openingLikeElement an JSX opening-like element we want to try resolve its stateless function if possible
18704-
* @param elementType a type of the opening-like JSX element, a result of resolving tagName in opening-like element.
18705-
* @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service;
18706-
* the function will fill it up with appropriate candidate signatures
18707-
* @return a resolved signature if we can find function matching function signature through resolve call or a first signature in the list of functions.
18708-
* otherwise return undefined if tag-name of the opening-like element doesn't have call signatures
18709-
*/
18710-
function resolveStatelessJsxOpeningLikeElement(openingLikeElement: JsxOpeningLikeElement, elementType: Type, candidatesOutArray: Signature[]): Signature | undefined {
18711-
// If this function is called from language service, elementType can be a union type. This is not possible if the function is called from compiler (see: resolveCustomJsxElementAttributesType)
18712-
if (elementType.flags & TypeFlags.Union) {
18713-
const types = (elementType as UnionType).types;
18714-
let result: Signature;
18715-
for (const type of types) {
18716-
result = result || resolveStatelessJsxOpeningLikeElement(openingLikeElement, type, candidatesOutArray);
18717-
}
18718-
18719-
return result;
18720-
}
18721-
1872218736
const callSignatures = elementType && getSignaturesOfType(elementType, SignatureKind.Call);
1872318737
if (callSignatures && callSignatures.length > 0) {
1872418738
return resolveCall(openingLikeElement, callSignatures, candidatesOutArray);
@@ -18740,7 +18754,18 @@ namespace ts {
1874018754
case SyntaxKind.JsxOpeningElement:
1874118755
case SyntaxKind.JsxSelfClosingElement:
1874218756
// This code-path is called by language service
18743-
return resolveStatelessJsxOpeningLikeElement(node, checkExpression(node.tagName), candidatesOutArray) || unknownSignature;
18757+
const exprTypes = checkExpression(node.tagName);
18758+
return forEachType(exprTypes, exprType => {
18759+
const sfcResult = getResolvedJsxStatelessFunctionSignature(node, exprType, candidatesOutArray);
18760+
if (sfcResult && sfcResult !== unknownSignature) {
18761+
return sfcResult;
18762+
}
18763+
const sigs = getInstantiatedJsxSignatures(node, exprType);
18764+
if (candidatesOutArray && length(sigs)) {
18765+
candidatesOutArray.push(...sigs);
18766+
}
18767+
return length(sigs) ? sigs[0] : unknownSignature;
18768+
}) || unknownSignature;
1874418769
}
1874518770
Debug.assertNever(node, "Branch in 'resolveSignature' should be unreachable.");
1874618771
}

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3625,6 +3625,7 @@ namespace ts {
36253625
flags?: NodeCheckFlags; // Set of flags specific to Node
36263626
resolvedType?: Type; // Cached type of type node
36273627
resolvedSignature?: Signature; // Cached signature of signature node or call expression
3628+
resolvedSignatures?: Map<Signature[]>; // Cached signatures of jsx node
36283629
resolvedSymbol?: Symbol; // Cached name resolution result
36293630
resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
36303631
maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate

src/compiler/utilities.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -3093,11 +3093,13 @@ namespace ts {
30933093
* Gets the effective type parameters. If the node was parsed in a
30943094
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
30953095
*/
3096-
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
3097-
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : undefined);
3096+
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters | JSDocTypedefTag) {
3097+
return isJSDocTypedefTag(node)
3098+
? getJSDocTypeParameterDeclarations(node)
3099+
: node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : undefined);
30983100
}
30993101

3100-
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
3102+
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters | JSDocTypedefTag) {
31013103
const templateTag = getJSDocTemplateTag(node);
31023104
return templateTag && templateTag.typeParameters;
31033105
}

tests/baselines/reference/checkJsdocTypedefInParamTag1.types

+12-12
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@
1010
* @param {Opts} opts
1111
*/
1212
function foo(opts) {
13-
>foo : (opts: { x: string; y?: string; z?: string; w?: string; }) => void
14-
>opts : { x: string; y?: string; z?: string; w?: string; }
13+
>foo : (opts: Opts) => void
14+
>opts : Opts
1515

1616
opts.x;
1717
>opts.x : string
18-
>opts : { x: string; y?: string; z?: string; w?: string; }
18+
>opts : Opts
1919
>x : string
2020
}
2121

2222
foo({x: 'abc'});
2323
>foo({x: 'abc'}) : void
24-
>foo : (opts: { x: string; y?: string; z?: string; w?: string; }) => void
24+
>foo : (opts: Opts) => void
2525
>{x: 'abc'} : { x: string; }
2626
>x : string
2727
>'abc' : "abc"
@@ -34,18 +34,18 @@ foo({x: 'abc'});
3434
* @param {AnotherOpts} opts
3535
*/
3636
function foo1(opts) {
37-
>foo1 : (opts: { anotherX: string; anotherY?: string; }) => void
38-
>opts : { anotherX: string; anotherY?: string; }
37+
>foo1 : (opts: AnotherOpts) => void
38+
>opts : AnotherOpts
3939

4040
opts.anotherX;
4141
>opts.anotherX : string
42-
>opts : { anotherX: string; anotherY?: string; }
42+
>opts : AnotherOpts
4343
>anotherX : string
4444
}
4545

4646
foo1({anotherX: "world"});
4747
>foo1({anotherX: "world"}) : void
48-
>foo1 : (opts: { anotherX: string; anotherY?: string; }) => void
48+
>foo1 : (opts: AnotherOpts) => void
4949
>{anotherX: "world"} : { anotherX: string; }
5050
>anotherX : string
5151
>"world" : "world"
@@ -60,17 +60,17 @@ foo1({anotherX: "world"});
6060
* @param {Opts1} opts
6161
*/
6262
function foo2(opts) {
63-
>foo2 : (opts: { x: string; y?: string; z?: string; w?: string; }) => void
64-
>opts : { x: string; y?: string; z?: string; w?: string; }
63+
>foo2 : (opts: Opts1) => void
64+
>opts : Opts1
6565

6666
opts.x;
6767
>opts.x : string
68-
>opts : { x: string; y?: string; z?: string; w?: string; }
68+
>opts : Opts1
6969
>x : string
7070
}
7171
foo2({x: 'abc'});
7272
>foo2({x: 'abc'}) : void
73-
>foo2 : (opts: { x: string; y?: string; z?: string; w?: string; }) => void
73+
>foo2 : (opts: Opts1) => void
7474
>{x: 'abc'} : { x: string; }
7575
>x : string
7676
>'abc' : "abc"

0 commit comments

Comments
 (0)