Skip to content

Commit f5dbcb7

Browse files
authored
Resolve more jsdoc value references as types (microsoft#34515)
* Resolve more jsdoc value references as types * add test
1 parent 262ec61 commit f5dbcb7

7 files changed

+53
-32
lines changed

src/compiler/checker.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -10740,7 +10740,7 @@ namespace ts {
1074010740
errorType;
1074110741
}
1074210742
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
10743-
const jsdocType = getTypeFromJSAlias(node, symbol);
10743+
const jsdocType = getTypeFromJSDocValueReference(node, symbol);
1074410744
if (jsdocType) {
1074510745
return jsdocType;
1074610746
}
@@ -10754,19 +10754,25 @@ namespace ts {
1075410754
}
1075510755

1075610756
/**
10757-
* A JSdoc TypeReference may be to a value imported from commonjs.
10758-
* These should really be aliases, but this special-case code fakes alias resolution
10759-
* by producing a type from a value.
10757+
* A JSdoc TypeReference may be to a value, but resolve it as a type anyway.
10758+
* Note: If the value is imported from commonjs, it should really be an alias,
10759+
* but this function fakes special-case code fakes alias resolution as well.
1076010760
*/
10761-
function getTypeFromJSAlias(node: NodeWithTypeArguments, symbol: Symbol): Type | undefined {
10761+
function getTypeFromJSDocValueReference(node: NodeWithTypeArguments, symbol: Symbol): Type | undefined {
1076210762
const valueType = getTypeOfSymbol(symbol);
10763-
const typeType =
10764-
valueType.symbol &&
10765-
valueType.symbol !== symbol && // Make sure this is a commonjs export by checking that symbol -> type -> symbol doesn't roundtrip.
10766-
getTypeReferenceType(node, valueType.symbol);
10767-
if (typeType) {
10768-
return getSymbolLinks(symbol).resolvedJSDocType = typeType;
10763+
let typeType = valueType;
10764+
if (symbol.valueDeclaration) {
10765+
const decl = getRootDeclaration(symbol.valueDeclaration);
10766+
const isRequireAlias = isVariableDeclaration(decl)
10767+
&& decl.initializer
10768+
&& isCallExpression(decl.initializer)
10769+
&& isRequireCall(decl.initializer, /*requireStringLiteralLikeArgument*/ true)
10770+
&& valueType.symbol;
10771+
if (isRequireAlias) {
10772+
typeType = getTypeReferenceType(node, valueType.symbol);
10773+
}
1076910774
}
10775+
return getSymbolLinks(symbol).resolvedJSDocType = typeType;
1077010776
}
1077110777

1077210778
function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) {

src/compiler/utilities.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1819,9 +1819,9 @@ namespace ts {
18191819
* exactly one argument (of the form 'require("name")').
18201820
* This function does not test if the node is in a JavaScript file or not.
18211821
*/
1822-
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] };
1823-
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: boolean): callExpression is CallExpression;
1824-
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: boolean): callExpression is CallExpression {
1822+
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] };
1823+
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression;
1824+
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression {
18251825
if (callExpression.kind !== SyntaxKind.CallExpression) {
18261826
return false;
18271827
}
@@ -1835,7 +1835,7 @@ namespace ts {
18351835
return false;
18361836
}
18371837
const arg = args[0];
1838-
return !checkArgumentIsStringLiteralLike || isStringLiteralLike(arg);
1838+
return !requireStringLiteralLikeArgument || isStringLiteralLike(arg);
18391839
}
18401840

18411841
export function isSingleOrDoubleQuote(charCode: number) {

tests/baselines/reference/jsdocTypeNongenericInstantiationAttempt.errors.txt

+1-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ tests/cases/compiler/index4.js(2,19): error TS2315: Type 'Function' is not gener
55
tests/cases/compiler/index5.js(2,19): error TS2315: Type 'String' is not generic.
66
tests/cases/compiler/index6.js(2,19): error TS2315: Type 'Number' is not generic.
77
tests/cases/compiler/index7.js(2,19): error TS2315: Type 'Object' is not generic.
8-
tests/cases/compiler/index8.js(4,12): error TS2749: 'fn' refers to a value, but is being used as a type here.
98
tests/cases/compiler/index8.js(4,15): error TS2304: Cannot find name 'T'.
109

1110

@@ -84,13 +83,11 @@ tests/cases/compiler/index8.js(4,15): error TS2304: Cannot find name 'T'.
8483
return 'Hello ' + somebody;
8584
}
8685

87-
==== tests/cases/compiler/index8.js (2 errors) ====
86+
==== tests/cases/compiler/index8.js (1 errors) ====
8887
function fn() {}
8988

9089
/**
9190
* @param {fn<T>} somebody
92-
~~
93-
!!! error TS2749: 'fn' refers to a value, but is being used as a type here.
9491
~
9592
!!! error TS2304: Cannot find name 'T'.
9693
*/

tests/baselines/reference/jsdocTypeReferenceExports.errors.txt

-13
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/conformance/jsdoc/foo.js ===
2+
/** @param {Image} image */
3+
function process(image) {
4+
>process : Symbol(process, Decl(foo.js, 0, 0))
5+
>image : Symbol(image, Decl(foo.js, 1, 17))
6+
7+
return new image(1, 1)
8+
>image : Symbol(image, Decl(foo.js, 1, 17))
9+
}
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/conformance/jsdoc/foo.js ===
2+
/** @param {Image} image */
3+
function process(image) {
4+
>process : (image: new (width?: number, height?: number) => HTMLImageElement) => HTMLImageElement
5+
>image : new (width?: number, height?: number) => HTMLImageElement
6+
7+
return new image(1, 1)
8+
>new image(1, 1) : HTMLImageElement
9+
>image : new (width?: number, height?: number) => HTMLImageElement
10+
>1 : 1
11+
>1 : 1
12+
}
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// @Filename: foo.js
2+
// @noEmit: true
3+
// @allowJs: true
4+
// @checkJs: true
5+
/** @param {Image} image */
6+
function process(image) {
7+
return new image(1, 1)
8+
}

0 commit comments

Comments
 (0)