-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Fix type when annotated with a JSDoc function type #22692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
aeaf83c
d79b4df
34c977e
dfbae12
1f6106b
e09bd63
deba8e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6699,7 +6699,13 @@ namespace ts { | |
let hasThisParameter: boolean; | ||
const iife = getImmediatelyInvokedFunctionExpression(declaration); | ||
const isJSConstructSignature = isJSDocConstructSignature(declaration); | ||
const isUntypedSignatureInJSFile = !iife && !isJSConstructSignature && isInJavaScriptFile(declaration) && !hasJSDocParameterTags(declaration); | ||
const isUntypedSignatureInJSFile = !iife && | ||
!isJSConstructSignature && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch. Removed. |
||
isInJavaScriptFile(declaration) && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than skipping (some of the) the type-space signature declaration kinds, can we just check if it is a concrete declaration kind? That is the point here, right; to exclude type-space declaration kinds? export type ConcreteSignatureDeclaration =
| FunctionDeclaration
| MethodDeclaration
| ConstructorDeclaration
| AccessorDeclaration
| FunctionExpression
| ArrowFunction;
export function isConcreteSignatureDeclaration(declaration: Node): node is ConcreteSignatureDeclaration {
return isFunctionExpression(declaration) || isArrowFunction(declaration) || isMethodOrAccessor(declaration) || isFunctionDeclaration(declaration) || isConstructorDeclaration(declaration);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, a type signature is by definition not untyped, even if it's in a JS file. I used your code in the new commit. |
||
declaration.kind !== SyntaxKind.FunctionType && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
declaration.kind !== SyntaxKind.JSDocFunctionType && | ||
!hasJSDocParameterTags(declaration) && | ||
!getJSDocType(declaration); | ||
|
||
// If this is a JSDoc construct signature, then skip the first parameter in the | ||
// parameter list. The first parameter represents the return type of the construct | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4510,6 +4510,15 @@ namespace ts { | |
let tag: JSDocTypeTag | JSDocParameterTag | undefined = getFirstJSDocTag(node, isJSDocTypeTag); | ||
if (!tag && isParameter(node)) { | ||
tag = find(getJSDocParameterTags(node), tag => !!tag.typeExpression); | ||
if (!tag && isFunctionLike(node.parent)) { | ||
const typeTag = getJSDocTypeTag(node.parent); | ||
if (typeTag && typeTag.typeExpression && isFunctionLike(typeTag.typeExpression.type)) { | ||
const i = node.parent.parameters.indexOf(node); | ||
if (i > -1) { | ||
return typeTag.typeExpression.type.parameters[i].type; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs a length check -- we don't know that |
||
} | ||
} | ||
} | ||
} | ||
|
||
return tag && tag.typeExpression && tag.typeExpression.type; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
tests/cases/conformance/jsdoc/a.js(3,5): error TS2322: Type '1' is not assignable to type 'string'. | ||
tests/cases/conformance/jsdoc/a.js(7,5): error TS2322: Type '1' is not assignable to type 'string'. | ||
|
||
|
||
==== tests/cases/conformance/jsdoc/a.js (2 errors) ==== | ||
/** @type {function(string): void} */ | ||
const f = (value) => { | ||
value = 1 // should error | ||
~~~~~ | ||
!!! error TS2322: Type '1' is not assignable to type 'string'. | ||
}; | ||
/** @type {(s: string) => void} */ | ||
function g(s) { | ||
s = 1 // Should error | ||
~ | ||
!!! error TS2322: Type '1' is not assignable to type 'string'. | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
=== tests/cases/conformance/jsdoc/a.js === | ||
/** @type {function(string): void} */ | ||
const f = (value) => { | ||
>f : Symbol(f, Decl(a.js, 1, 5)) | ||
>value : Symbol(value, Decl(a.js, 1, 11)) | ||
|
||
value = 1 // should error | ||
>value : Symbol(value, Decl(a.js, 1, 11)) | ||
|
||
}; | ||
/** @type {(s: string) => void} */ | ||
function g(s) { | ||
>g : Symbol(g, Decl(a.js, 3, 2)) | ||
>s : Symbol(s, Decl(a.js, 5, 11)) | ||
|
||
s = 1 // Should error | ||
>s : Symbol(s, Decl(a.js, 5, 11)) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
=== tests/cases/conformance/jsdoc/a.js === | ||
/** @type {function(string): void} */ | ||
const f = (value) => { | ||
>f : (arg0: string) => void | ||
>(value) => { value = 1 // should error} : (value: string) => void | ||
>value : string | ||
|
||
value = 1 // should error | ||
>value = 1 : 1 | ||
>value : string | ||
>1 : 1 | ||
|
||
}; | ||
/** @type {(s: string) => void} */ | ||
function g(s) { | ||
>g : (s: string) => void | ||
>s : string | ||
|
||
s = 1 // Should error | ||
>s = 1 : 1 | ||
>s : string | ||
>1 : 1 | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
tests/cases/conformance/jsdoc/a.js(8,1): error TS2554: Expected 1 arguments, but got 0. | ||
tests/cases/conformance/jsdoc/a.js(9,1): error TS2554: Expected 1 arguments, but got 0. | ||
|
||
|
||
==== tests/cases/conformance/jsdoc/a.js (2 errors) ==== | ||
/** @type {function(string): void} */ | ||
const f = (value) => { | ||
}; | ||
/** @type {(s: string) => void} */ | ||
function g(s) { | ||
} | ||
|
||
f() // should error | ||
~~~ | ||
!!! error TS2554: Expected 1 arguments, but got 0. | ||
g() // should error | ||
~~~ | ||
!!! error TS2554: Expected 1 arguments, but got 0. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
=== tests/cases/conformance/jsdoc/a.js === | ||
/** @type {function(string): void} */ | ||
const f = (value) => { | ||
>f : Symbol(f, Decl(a.js, 1, 5)) | ||
>value : Symbol(value, Decl(a.js, 1, 11)) | ||
|
||
}; | ||
/** @type {(s: string) => void} */ | ||
function g(s) { | ||
>g : Symbol(g, Decl(a.js, 2, 2)) | ||
>s : Symbol(s, Decl(a.js, 4, 11)) | ||
} | ||
|
||
f() // should error | ||
>f : Symbol(f, Decl(a.js, 1, 5)) | ||
|
||
g() // should error | ||
>g : Symbol(g, Decl(a.js, 2, 2)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
=== tests/cases/conformance/jsdoc/a.js === | ||
/** @type {function(string): void} */ | ||
const f = (value) => { | ||
>f : (arg0: string) => void | ||
>(value) => {} : (value: string) => void | ||
>value : string | ||
|
||
}; | ||
/** @type {(s: string) => void} */ | ||
function g(s) { | ||
>g : (s: string) => void | ||
>s : string | ||
} | ||
|
||
f() // should error | ||
>f() : void | ||
>f : (arg0: string) => void | ||
|
||
g() // should error | ||
>g() : void | ||
>g : (s: string) => void | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// @allowJs: true | ||
// @checkJs: true | ||
// @noEmit: true | ||
// @strict: true | ||
// @noImplicitAny: true | ||
// @Filename: a.js | ||
|
||
/** @type {function(string): void} */ | ||
const f = (value) => { | ||
value = 1 // should error | ||
}; | ||
/** @type {(s: string) => void} */ | ||
function g(s) { | ||
s = 1 // Should error | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// @allowJs: true | ||
// @checkJs: true | ||
// @noEmit: true | ||
// @strict: true | ||
// @noImplicitAny: true | ||
// @Filename: a.js | ||
|
||
/** @type {function(string): void} */ | ||
const f = (value) => { | ||
}; | ||
/** @type {(s: string) => void} */ | ||
function g(s) { | ||
} | ||
|
||
f() // should error | ||
g() // should error |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What case is handling when the declaration is an object literal with call/construct signatures, (ie,
{(): void}
)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!getJSDocType(declaration)
. I'll add a test case. Note that the new code ingetJSDocType
doesn't look deep enough to get type of the parameter out of the method declaration inside the object literal type.