Resolve more jsdoc value references as types (#34515)

* Resolve more jsdoc value references as types

* add test
This commit is contained in:
Nathan Shively-Sanders 2019-10-17 11:21:34 -07:00 committed by GitHub
parent 262ec6171d
commit f5dbcb78af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 32 deletions

View File

@ -10740,7 +10740,7 @@ namespace ts {
errorType;
}
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
const jsdocType = getTypeFromJSAlias(node, symbol);
const jsdocType = getTypeFromJSDocValueReference(node, symbol);
if (jsdocType) {
return jsdocType;
}
@ -10754,19 +10754,25 @@ namespace ts {
}
/**
* A JSdoc TypeReference may be to a value imported from commonjs.
* These should really be aliases, but this special-case code fakes alias resolution
* by producing a type from a value.
* A JSdoc TypeReference may be to a value, but resolve it as a type anyway.
* Note: If the value is imported from commonjs, it should really be an alias,
* but this function fakes special-case code fakes alias resolution as well.
*/
function getTypeFromJSAlias(node: NodeWithTypeArguments, symbol: Symbol): Type | undefined {
function getTypeFromJSDocValueReference(node: NodeWithTypeArguments, symbol: Symbol): Type | undefined {
const valueType = getTypeOfSymbol(symbol);
const typeType =
valueType.symbol &&
valueType.symbol !== symbol && // Make sure this is a commonjs export by checking that symbol -> type -> symbol doesn't roundtrip.
getTypeReferenceType(node, valueType.symbol);
if (typeType) {
return getSymbolLinks(symbol).resolvedJSDocType = typeType;
let typeType = valueType;
if (symbol.valueDeclaration) {
const decl = getRootDeclaration(symbol.valueDeclaration);
const isRequireAlias = isVariableDeclaration(decl)
&& decl.initializer
&& isCallExpression(decl.initializer)
&& isRequireCall(decl.initializer, /*requireStringLiteralLikeArgument*/ true)
&& valueType.symbol;
if (isRequireAlias) {
typeType = getTypeReferenceType(node, valueType.symbol);
}
}
return getSymbolLinks(symbol).resolvedJSDocType = typeType;
}
function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) {

View File

@ -1819,9 +1819,9 @@ namespace ts {
* exactly one argument (of the form 'require("name")').
* This function does not test if the node is in a JavaScript file or not.
*/
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] };
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: boolean): callExpression is CallExpression;
export function isRequireCall(callExpression: Node, checkArgumentIsStringLiteralLike: boolean): callExpression is CallExpression {
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] };
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression;
export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression {
if (callExpression.kind !== SyntaxKind.CallExpression) {
return false;
}
@ -1835,7 +1835,7 @@ namespace ts {
return false;
}
const arg = args[0];
return !checkArgumentIsStringLiteralLike || isStringLiteralLike(arg);
return !requireStringLiteralLikeArgument || isStringLiteralLike(arg);
}
export function isSingleOrDoubleQuote(charCode: number) {

View File

@ -5,7 +5,6 @@ tests/cases/compiler/index4.js(2,19): error TS2315: Type 'Function' is not gener
tests/cases/compiler/index5.js(2,19): error TS2315: Type 'String' is not generic.
tests/cases/compiler/index6.js(2,19): error TS2315: Type 'Number' is not generic.
tests/cases/compiler/index7.js(2,19): error TS2315: Type 'Object' is not generic.
tests/cases/compiler/index8.js(4,12): error TS2749: 'fn' refers to a value, but is being used as a type here.
tests/cases/compiler/index8.js(4,15): error TS2304: Cannot find name 'T'.
@ -84,13 +83,11 @@ tests/cases/compiler/index8.js(4,15): error TS2304: Cannot find name 'T'.
return 'Hello ' + somebody;
}
==== tests/cases/compiler/index8.js (2 errors) ====
==== tests/cases/compiler/index8.js (1 errors) ====
function fn() {}
/**
* @param {fn<T>} somebody
~~
!!! error TS2749: 'fn' refers to a value, but is being used as a type here.
~
!!! error TS2304: Cannot find name 'T'.
*/

View File

@ -1,13 +0,0 @@
tests/cases/conformance/jsdoc/bug27342.js(3,11): error TS2709: Cannot use namespace 'exports' as a type.
==== tests/cases/conformance/jsdoc/bug27342.js (1 errors) ====
module.exports = {}
/**
* @type {exports}
~~~~~~~
!!! error TS2709: Cannot use namespace 'exports' as a type.
*/
var x

View File

@ -0,0 +1,10 @@
=== tests/cases/conformance/jsdoc/foo.js ===
/** @param {Image} image */
function process(image) {
>process : Symbol(process, Decl(foo.js, 0, 0))
>image : Symbol(image, Decl(foo.js, 1, 17))
return new image(1, 1)
>image : Symbol(image, Decl(foo.js, 1, 17))
}

View File

@ -0,0 +1,13 @@
=== tests/cases/conformance/jsdoc/foo.js ===
/** @param {Image} image */
function process(image) {
>process : (image: new (width?: number, height?: number) => HTMLImageElement) => HTMLImageElement
>image : new (width?: number, height?: number) => HTMLImageElement
return new image(1, 1)
>new image(1, 1) : HTMLImageElement
>image : new (width?: number, height?: number) => HTMLImageElement
>1 : 1
>1 : 1
}

View File

@ -0,0 +1,8 @@
// @Filename: foo.js
// @noEmit: true
// @allowJs: true
// @checkJs: true
/** @param {Image} image */
function process(image) {
return new image(1, 1)
}