Skip to content

Commit d613748

Browse files
authored
fix(45919): allow using JSDoc types for arrow function with type predicate (#45952)
1 parent e0f436c commit d613748

File tree

6 files changed

+178
-8
lines changed

6 files changed

+178
-8
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23206,10 +23206,10 @@ namespace ts {
2320623206
return isLengthPushOrUnshift || isElementAssignment;
2320723207
}
2320823208

23209-
function isDeclarationWithExplicitTypeAnnotation(declaration: Declaration) {
23210-
return (declaration.kind === SyntaxKind.VariableDeclaration || declaration.kind === SyntaxKind.Parameter ||
23211-
declaration.kind === SyntaxKind.PropertyDeclaration || declaration.kind === SyntaxKind.PropertySignature) &&
23212-
!!getEffectiveTypeAnnotationNode(declaration as VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature);
23209+
function isDeclarationWithExplicitTypeAnnotation(node: Declaration) {
23210+
return (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isParameter(node)) &&
23211+
!!(getEffectiveTypeAnnotationNode(node) ||
23212+
isInJSFile(node) && hasInitializer(node) && node.initializer && isFunctionExpressionOrArrowFunction(node.initializer) && getEffectiveReturnTypeNode(node.initializer));
2321323213
}
2321423214

2321523215
function getExplicitTypeOfSymbol(symbol: Symbol, diagnostic?: Diagnostic) {
@@ -26570,10 +26570,6 @@ namespace ts {
2657026570
return !hasEffectiveRestParameter(signature) && getParameterCount(signature) < targetParameterCount;
2657126571
}
2657226572

26573-
function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction {
26574-
return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction;
26575-
}
26576-
2657726573
function getContextualSignatureForFunctionLikeDeclaration(node: FunctionLikeDeclaration): Signature | undefined {
2657826574
// Only function expressions, arrow functions, and object literal methods are contextually typed.
2657926575
return isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node)

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7415,4 +7415,8 @@ namespace ts {
74157415
const declaration = symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration);
74167416
return !!declaration && (isParameter(declaration) || isCatchClauseVariableDeclaration(declaration));
74177417
}
7418+
7419+
export function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction {
7420+
return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction;
7421+
}
74187422
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [assertionTypePredicates2.js]
2+
/**
3+
* @typedef {{ x: number }} A
4+
*/
5+
6+
/**
7+
* @typedef { A & { y: number } } B
8+
*/
9+
10+
/**
11+
* @param {A} a
12+
* @returns { asserts a is B }
13+
*/
14+
const foo = (a) => {
15+
if (/** @type { B } */ (a).y !== 0) throw TypeError();
16+
return undefined;
17+
};
18+
19+
export const main = () => {
20+
/** @type { A } */
21+
const a = { x: 1 };
22+
foo(a);
23+
};
24+
25+
26+
//// [assertionTypePredicates2.js]
27+
"use strict";
28+
/**
29+
* @typedef {{ x: number }} A
30+
*/
31+
exports.__esModule = true;
32+
exports.main = void 0;
33+
/**
34+
* @typedef { A & { y: number } } B
35+
*/
36+
/**
37+
* @param {A} a
38+
* @returns { asserts a is B }
39+
*/
40+
var foo = function (a) {
41+
if ( /** @type { B } */(a).y !== 0)
42+
throw TypeError();
43+
return undefined;
44+
};
45+
var main = function () {
46+
/** @type { A } */
47+
var a = { x: 1 };
48+
foo(a);
49+
};
50+
exports.main = main;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/conformance/controlFlow/assertionTypePredicates2.js ===
2+
/**
3+
* @typedef {{ x: number }} A
4+
*/
5+
6+
/**
7+
* @typedef { A & { y: number } } B
8+
*/
9+
10+
/**
11+
* @param {A} a
12+
* @returns { asserts a is B }
13+
*/
14+
const foo = (a) => {
15+
>foo : Symbol(foo, Decl(assertionTypePredicates2.js, 12, 5))
16+
>a : Symbol(a, Decl(assertionTypePredicates2.js, 12, 13))
17+
18+
if (/** @type { B } */ (a).y !== 0) throw TypeError();
19+
>(a).y : Symbol(y, Decl(assertionTypePredicates2.js, 5, 19))
20+
>a : Symbol(a, Decl(assertionTypePredicates2.js, 12, 13))
21+
>y : Symbol(y, Decl(assertionTypePredicates2.js, 5, 19))
22+
>TypeError : Symbol(TypeError, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
23+
24+
return undefined;
25+
>undefined : Symbol(undefined)
26+
27+
};
28+
29+
export const main = () => {
30+
>main : Symbol(main, Decl(assertionTypePredicates2.js, 17, 12))
31+
32+
/** @type { A } */
33+
const a = { x: 1 };
34+
>a : Symbol(a, Decl(assertionTypePredicates2.js, 19, 9))
35+
>x : Symbol(x, Decl(assertionTypePredicates2.js, 19, 15))
36+
37+
foo(a);
38+
>foo : Symbol(foo, Decl(assertionTypePredicates2.js, 12, 5))
39+
>a : Symbol(a, Decl(assertionTypePredicates2.js, 19, 9))
40+
41+
};
42+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=== tests/cases/conformance/controlFlow/assertionTypePredicates2.js ===
2+
/**
3+
* @typedef {{ x: number }} A
4+
*/
5+
6+
/**
7+
* @typedef { A & { y: number } } B
8+
*/
9+
10+
/**
11+
* @param {A} a
12+
* @returns { asserts a is B }
13+
*/
14+
const foo = (a) => {
15+
>foo : (a: A) => asserts a is B
16+
>(a) => { if (/** @type { B } */ (a).y !== 0) throw TypeError(); return undefined;} : (a: A) => asserts a is B
17+
>a : A
18+
19+
if (/** @type { B } */ (a).y !== 0) throw TypeError();
20+
>(a).y !== 0 : boolean
21+
>(a).y : number
22+
>(a) : B
23+
>a : A
24+
>y : number
25+
>0 : 0
26+
>TypeError() : TypeError
27+
>TypeError : TypeErrorConstructor
28+
29+
return undefined;
30+
>undefined : undefined
31+
32+
};
33+
34+
export const main = () => {
35+
>main : () => void
36+
>() => { /** @type { A } */ const a = { x: 1 }; foo(a);} : () => void
37+
38+
/** @type { A } */
39+
const a = { x: 1 };
40+
>a : A
41+
>{ x: 1 } : { x: number; }
42+
>x : number
43+
>1 : 1
44+
45+
foo(a);
46+
>foo(a) : void
47+
>foo : (a: A) => asserts a is B
48+
>a : A
49+
50+
};
51+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @outDir: ./out
4+
// @filename: assertionTypePredicates2.js
5+
6+
/**
7+
* @typedef {{ x: number }} A
8+
*/
9+
10+
/**
11+
* @typedef { A & { y: number } } B
12+
*/
13+
14+
/**
15+
* @param {A} a
16+
* @returns { asserts a is B }
17+
*/
18+
const foo = (a) => {
19+
if (/** @type { B } */ (a).y !== 0) throw TypeError();
20+
return undefined;
21+
};
22+
23+
export const main = () => {
24+
/** @type { A } */
25+
const a = { x: 1 };
26+
foo(a);
27+
};

0 commit comments

Comments
 (0)