Skip to content

Commit 3107e11

Browse files
committed
Reuse getEffectiveTypeParameterHost in isTypeParameterPossiblyReferenced
1 parent 230925a commit 3107e11

File tree

4 files changed

+244
-9
lines changed

4 files changed

+244
-9
lines changed

src/compiler/checker.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16217,8 +16217,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1621716217
}
1621816218

1621916219
function getEffectiveTypeParameterHost(typeParameter: TypeParameter) {
16220-
const tp = getDeclarationOfKind<TypeParameterDeclaration>(typeParameter.symbol, SyntaxKind.TypeParameter)!;
16221-
return isJSDocTemplateTag(tp.parent) ? getEffectiveJSDocHost(tp.parent) : tp.parent;
16220+
const declarations = typeParameter.symbol.declarations;
16221+
if (!declarations || declarations.length !== 1) {
16222+
return;
16223+
}
16224+
const tpDeclaration = getDeclarationOfKind<TypeParameterDeclaration>(typeParameter.symbol, SyntaxKind.TypeParameter);
16225+
if (!tpDeclaration) {
16226+
// Type parameter is the this type, and its declaration is the class declaration.
16227+
return typeParameter.isThisType ? declarations[0].parent : undefined;
16228+
}
16229+
return isJSDocTemplateTag(tpDeclaration.parent) ? getEffectiveJSDocHost(tpDeclaration.parent) : tpDeclaration.parent;
1622216230
}
1622316231

1622416232
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined {
@@ -20193,7 +20201,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2019320201
// type parameter, or if the node contains type queries that we can't prove couldn't contain references to the type parameter,
2019420202
// we consider the type parameter possibly referenced.
2019520203
if (tp.symbol && tp.symbol.declarations && tp.symbol.declarations.length === 1) {
20196-
const container = tp.symbol.declarations[0].parent;
20204+
const container = getEffectiveTypeParameterHost(tp);
2019720205
for (let n = node; n !== container; n = n.parent) {
2019820206
if (!n || n.kind === SyntaxKind.Block || n.kind === SyntaxKind.ConditionalType && forEachChild((n as ConditionalTypeNode).extendsType, containsReference)) {
2019920207
return true;
@@ -20214,12 +20222,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2021420222
const firstIdentifier = getFirstIdentifier(entityName);
2021520223
if (!isThisIdentifier(firstIdentifier)) { // Don't attempt to analyze typeof this.xxx
2021620224
const firstIdentifierSymbol = getResolvedSymbol(firstIdentifier);
20217-
const tpDeclaration = tp.symbol.declarations![0]; // There is exactly one declaration, otherwise `containsReference` is not called
20218-
const tpScope = tpDeclaration.kind === SyntaxKind.TypeParameter ? tpDeclaration.parent : // Type parameter is a regular type parameter, e.g. foo<T>
20219-
tp.isThisType ? tpDeclaration : // Type parameter is the this type, and its declaration is the class declaration.
20220-
undefined; // Type parameter's declaration was unrecognized, e.g. comes from JSDoc annotation.
20221-
if (firstIdentifierSymbol.declarations && tpScope) {
20222-
return some(firstIdentifierSymbol.declarations, idDecl => isNodeDescendantOf(idDecl, tpScope)) ||
20225+
const tpHost = getEffectiveTypeParameterHost(tp);
20226+
if (firstIdentifierSymbol.declarations && tpHost) {
20227+
return some(firstIdentifierSymbol.declarations, idDecl => isNodeDescendantOf(idDecl, tpHost)) ||
2022320228
some((node as TypeQueryNode).typeArguments, containsReference);
2022420229
}
2022520230
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//// [tests/cases/compiler/inferFromReturnOfContextSensitiveFnJsDoc1.ts] ////
2+
3+
=== index.js ===
4+
/**
5+
* @template S
6+
* @param {(arg0: { observer: EO }) => S} callback
7+
* @param {Options} [options]
8+
* @returns {VC<S>}
9+
*/
10+
/*
11+
* @type { <S>(fn: (arg0: { observer: EO; }) => S, options?: Options) => VC<S> }
12+
*/
13+
function define(callback, options) {
14+
>define : Symbol(define, Decl(index.js, 0, 0))
15+
>callback : Symbol(callback, Decl(index.js, 9, 16))
16+
>options : Symbol(options, Decl(index.js, 9, 25))
17+
18+
const { name } = options ?? {};
19+
>name : Symbol(name, Decl(index.js, 10, 9))
20+
>options : Symbol(options, Decl(index.js, 9, 25))
21+
22+
const observer = new EO();
23+
>observer : Symbol(observer, Decl(index.js, 11, 7))
24+
>EO : Symbol(EO, Decl(index.js, 28, 1))
25+
26+
const state = callback({ observer });
27+
>state : Symbol(state, Decl(index.js, 12, 7))
28+
>callback : Symbol(callback, Decl(index.js, 9, 16))
29+
>observer : Symbol(observer, Decl(index.js, 12, 26))
30+
31+
return new VC(state);
32+
>VC : Symbol(VC, Decl(index.js, 14, 1))
33+
>state : Symbol(state, Decl(index.js, 12, 7))
34+
}
35+
36+
/**
37+
* @template S
38+
*/
39+
class VC {
40+
>VC : Symbol(VC, Decl(index.js, 14, 1))
41+
42+
/** @type {S} */
43+
state;
44+
>state : Symbol(VC.state, Decl(index.js, 19, 10))
45+
46+
/**
47+
* @param {S} state
48+
*/
49+
constructor(state) {
50+
>state : Symbol(state, Decl(index.js, 25, 14))
51+
52+
this.state = state;
53+
>this.state : Symbol(VC.state, Decl(index.js, 19, 10))
54+
>this : Symbol(VC, Decl(index.js, 14, 1))
55+
>state : Symbol(VC.state, Decl(index.js, 19, 10))
56+
>state : Symbol(state, Decl(index.js, 25, 14))
57+
}
58+
}
59+
60+
/** @typedef {{ name?: string }} Options */
61+
62+
class EO {}
63+
>EO : Symbol(EO, Decl(index.js, 28, 1))
64+
65+
const v1 = define((arg0) => true, { name: "default" });
66+
>v1 : Symbol(v1, Decl(index.js, 34, 5))
67+
>define : Symbol(define, Decl(index.js, 0, 0))
68+
>arg0 : Symbol(arg0, Decl(index.js, 34, 19))
69+
>name : Symbol(name, Decl(index.js, 34, 35))
70+
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//// [tests/cases/compiler/inferFromReturnOfContextSensitiveFnJsDoc1.ts] ////
2+
3+
=== index.js ===
4+
/**
5+
* @template S
6+
* @param {(arg0: { observer: EO }) => S} callback
7+
* @param {Options} [options]
8+
* @returns {VC<S>}
9+
*/
10+
/*
11+
* @type { <S>(fn: (arg0: { observer: EO; }) => S, options?: Options) => VC<S> }
12+
*/
13+
function define(callback, options) {
14+
>define : <S>(callback: (arg0: { observer: EO; }) => S, options?: Options) => VC<S>
15+
> : ^ ^^ ^^ ^^ ^^^ ^^^^^
16+
>callback : (arg0: { observer: EO; }) => S
17+
> : ^ ^^ ^^^^^
18+
>options : Options | undefined
19+
> : ^^^^^^^^^^^^^^^^^^^
20+
21+
const { name } = options ?? {};
22+
>name : string | undefined
23+
> : ^^^^^^^^^^^^^^^^^^
24+
>options ?? {} : Options
25+
> : ^^^^^^^
26+
>options : Options | undefined
27+
> : ^^^^^^^^^^^^^^^^^^^
28+
>{} : {}
29+
> : ^^
30+
31+
const observer = new EO();
32+
>observer : EO
33+
> : ^^
34+
>new EO() : EO
35+
> : ^^
36+
>EO : typeof EO
37+
> : ^^^^^^^^^
38+
39+
const state = callback({ observer });
40+
>state : S
41+
> : ^
42+
>callback({ observer }) : S
43+
> : ^
44+
>callback : (arg0: { observer: EO; }) => S
45+
> : ^ ^^ ^^^^^
46+
>{ observer } : { observer: EO; }
47+
> : ^^^^^^^^^^^^^^^^^
48+
>observer : EO
49+
> : ^^
50+
51+
return new VC(state);
52+
>new VC(state) : VC<S>
53+
> : ^^^^^
54+
>VC : typeof VC
55+
> : ^^^^^^^^^
56+
>state : S
57+
> : ^
58+
}
59+
60+
/**
61+
* @template S
62+
*/
63+
class VC {
64+
>VC : VC<S>
65+
> : ^^^^^
66+
67+
/** @type {S} */
68+
state;
69+
>state : S
70+
> : ^
71+
72+
/**
73+
* @param {S} state
74+
*/
75+
constructor(state) {
76+
>state : S
77+
> : ^
78+
79+
this.state = state;
80+
>this.state = state : S
81+
> : ^
82+
>this.state : S
83+
> : ^
84+
>this : this
85+
> : ^^^^
86+
>state : S
87+
> : ^
88+
>state : S
89+
> : ^
90+
}
91+
}
92+
93+
/** @typedef {{ name?: string }} Options */
94+
95+
class EO {}
96+
>EO : EO
97+
> : ^^
98+
99+
const v1 = define((arg0) => true, { name: "default" });
100+
>v1 : VC<boolean>
101+
> : ^^^^^^^^^^^
102+
>define((arg0) => true, { name: "default" }) : VC<boolean>
103+
> : ^^^^^^^^^^^
104+
>define : <S>(callback: (arg0: { observer: EO; }) => S, options?: Options) => VC<S>
105+
> : ^ ^^ ^^ ^^ ^^^ ^^^^^
106+
>(arg0) => true : (arg0: { observer: EO; }) => boolean
107+
> : ^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
108+
>arg0 : { observer: EO; }
109+
> : ^^^^^^^^^^^^ ^^^
110+
>true : true
111+
> : ^^^^
112+
>{ name: "default" } : { name: string; }
113+
> : ^^^^^^^^^^^^^^^^^
114+
>name : string
115+
> : ^^^^^^
116+
>"default" : "default"
117+
> : ^^^^^^^^^
118+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// @strict: true
2+
// @noEmit: true
3+
// @checkJs: true
4+
// @allowJs: true
5+
6+
// @filename: index.js
7+
8+
/**
9+
* @template S
10+
* @param {(arg0: { observer: EO }) => S} callback
11+
* @param {Options} [options]
12+
* @returns {VC<S>}
13+
*/
14+
/*
15+
* @type { <S>(fn: (arg0: { observer: EO; }) => S, options?: Options) => VC<S> }
16+
*/
17+
function define(callback, options) {
18+
const { name } = options ?? {};
19+
const observer = new EO();
20+
const state = callback({ observer });
21+
return new VC(state);
22+
}
23+
24+
/**
25+
* @template S
26+
*/
27+
class VC {
28+
/** @type {S} */
29+
state;
30+
/**
31+
* @param {S} state
32+
*/
33+
constructor(state) {
34+
this.state = state;
35+
}
36+
}
37+
38+
/** @typedef {{ name?: string }} Options */
39+
40+
class EO {}
41+
42+
const v1 = define((arg0) => true, { name: "default" });

0 commit comments

Comments
 (0)