From c37efd7a29c344cee9a85051705dd0c6d499026d Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 16 Oct 2019 09:40:00 -0700 Subject: [PATCH 1/2] Resolve more jsdoc value references as types --- src/compiler/checker.ts | 28 +++++++++++-------- src/compiler/utilities.ts | 8 +++--- ...eNongenericInstantiationAttempt.errors.txt | 5 +--- .../jsdocTypeReferenceExports.errors.txt | 13 --------- 4 files changed, 22 insertions(+), 32 deletions(-) delete mode 100644 tests/baselines/reference/jsdocTypeReferenceExports.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ec63501af75d9..f14c6e04dc960 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10746,7 +10746,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; } @@ -10760,19 +10760,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) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2fb1173deef3a..a841007d9d5df 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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) { diff --git a/tests/baselines/reference/jsdocTypeNongenericInstantiationAttempt.errors.txt b/tests/baselines/reference/jsdocTypeNongenericInstantiationAttempt.errors.txt index b3298f137f14f..5392d51114c39 100644 --- a/tests/baselines/reference/jsdocTypeNongenericInstantiationAttempt.errors.txt +++ b/tests/baselines/reference/jsdocTypeNongenericInstantiationAttempt.errors.txt @@ -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} somebody - ~~ -!!! error TS2749: 'fn' refers to a value, but is being used as a type here. ~ !!! error TS2304: Cannot find name 'T'. */ diff --git a/tests/baselines/reference/jsdocTypeReferenceExports.errors.txt b/tests/baselines/reference/jsdocTypeReferenceExports.errors.txt deleted file mode 100644 index 48d0321be71d9..0000000000000 --- a/tests/baselines/reference/jsdocTypeReferenceExports.errors.txt +++ /dev/null @@ -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 - - \ No newline at end of file From e0673d36f3d7841383839f534f410408a226dde5 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 16 Oct 2019 09:49:12 -0700 Subject: [PATCH 2/2] add test --- .../reference/jsdocTypeReferenceToValue.symbols | 10 ++++++++++ .../reference/jsdocTypeReferenceToValue.types | 13 +++++++++++++ .../conformance/jsdoc/jsdocTypeReferenceToValue.ts | 8 ++++++++ 3 files changed, 31 insertions(+) create mode 100644 tests/baselines/reference/jsdocTypeReferenceToValue.symbols create mode 100644 tests/baselines/reference/jsdocTypeReferenceToValue.types create mode 100644 tests/cases/conformance/jsdoc/jsdocTypeReferenceToValue.ts diff --git a/tests/baselines/reference/jsdocTypeReferenceToValue.symbols b/tests/baselines/reference/jsdocTypeReferenceToValue.symbols new file mode 100644 index 0000000000000..58facc1e821c3 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeReferenceToValue.symbols @@ -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)) +} + diff --git a/tests/baselines/reference/jsdocTypeReferenceToValue.types b/tests/baselines/reference/jsdocTypeReferenceToValue.types new file mode 100644 index 0000000000000..91f8e26d35d4b --- /dev/null +++ b/tests/baselines/reference/jsdocTypeReferenceToValue.types @@ -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 +} + diff --git a/tests/cases/conformance/jsdoc/jsdocTypeReferenceToValue.ts b/tests/cases/conformance/jsdoc/jsdocTypeReferenceToValue.ts new file mode 100644 index 0000000000000..4f3ba385ac58f --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTypeReferenceToValue.ts @@ -0,0 +1,8 @@ +// @Filename: foo.js +// @noEmit: true +// @allowJs: true +// @checkJs: true +/** @param {Image} image */ +function process(image) { + return new image(1, 1) +}