Skip to content

Commit a083c81

Browse files
authored
fix(no-undefined-types): liberalize checks to reallow for unknown properties on imports and defined globals; fixes #1412 (#1413)
1 parent 3d2d379 commit a083c81

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

docs/rules/no-undefined-types.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,5 +919,26 @@ class Foo {
919919
return "bar";
920920
}
921921
}
922+
923+
/* globals SomeGlobal, AnotherGlobal */
924+
import * as Ably from "ably"
925+
import Testing, { another as Another, stillMore as StillMore } from "testing"
926+
927+
export class Code {
928+
/** @type {Ably.Realtime} */
929+
static #client
930+
931+
/** @type {Testing.SomeMethod} */
932+
static #test
933+
934+
/** @type {Another.AnotherMethod} */
935+
static #test2
936+
937+
/** @type {StillMore.AnotherMethod} */
938+
static #test3
939+
940+
/** @type {AnotherGlobal.AnotherMethod} */
941+
static #test4
942+
}
922943
````
923944

src/rules/noUndefinedTypes.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,22 @@ export default iterateJsdoc(({
112112
.filter(Boolean));
113113
}
114114

115-
const comments = sourceCode.getAllComments()
115+
const allComments = sourceCode.getAllComments();
116+
const comments = allComments
116117
.filter((comment) => {
117118
return (/^\*\s/u).test(comment.value);
118119
})
119120
.map((commentNode) => {
120121
return parseComment(commentNode, '');
121122
});
122123

124+
const globals = allComments
125+
.filter((comment) => {
126+
return (/^\s*globals/u).test(comment.value);
127+
}).flatMap((commentNode) => {
128+
return commentNode.value.replace(/^\s*globals/u, '').trim().split(/,\s*/u);
129+
});
130+
123131
const typedefDeclarations = comments
124132
.flatMap((doc) => {
125133
return doc.tags.filter(({
@@ -242,6 +250,12 @@ export default iterateJsdoc(({
242250
return result;
243251
};
244252

253+
/**
254+
* We treat imports differently as we can't introspect their children.
255+
* @type {string[]}
256+
*/
257+
const imports = [];
258+
245259
const allDefinedTypes = new Set(globalScope.variables.map(({
246260
name,
247261
}) => {
@@ -256,8 +270,22 @@ export default iterateJsdoc(({
256270
}) => {
257271
return variables;
258272
}).map(({
273+
identifiers,
259274
name,
260275
}) => {
276+
if (
277+
[
278+
'ImportDefaultSpecifier',
279+
'ImportNamespaceSpecifier',
280+
'ImportSpecifier',
281+
].includes(
282+
/** @type {import('estree').Identifier & {parent: {type: string}}} */ (
283+
identifiers?.[0])?.parent?.type,
284+
)
285+
) {
286+
imports.push(name);
287+
}
288+
261289
return name;
262290
/* c8 ignore next */
263291
}) : [],
@@ -408,7 +436,12 @@ export default iterateJsdoc(({
408436
* _parent?: import('jsdoc-type-pratt-parser').NonRootResult
409437
* }}
410438
*/ (currNode)._parent;
411-
if (currNode && 'right' in currNode && currNode.right?.type === 'JsdocTypeProperty') {
439+
if (
440+
// Avoid appending for imports and globals since we don't want to
441+
// check their properties which may or may not exist
442+
!imports.includes(val) && !globals.includes(val) &&
443+
currNode && 'right' in currNode &&
444+
currNode.right?.type === 'JsdocTypeProperty') {
412445
val = val + '.' + currNode.right.value;
413446
}
414447
} while (currNode?.type === 'JsdocTypeNamePath');

test/rules/assertions/noUndefinedTypes.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,5 +1624,32 @@ export default /** @type {import('../index.js').TestCases} */ ({
16241624
ecmaVersion: 2_022,
16251625
},
16261626
},
1627+
{
1628+
code: `
1629+
/* globals SomeGlobal, AnotherGlobal */
1630+
import * as Ably from "ably"
1631+
import Testing, { another as Another, stillMore as StillMore } from "testing"
1632+
1633+
export class Code {
1634+
/** @type {Ably.Realtime} */
1635+
static #client
1636+
1637+
/** @type {Testing.SomeMethod} */
1638+
static #test
1639+
1640+
/** @type {Another.AnotherMethod} */
1641+
static #test2
1642+
1643+
/** @type {StillMore.AnotherMethod} */
1644+
static #test3
1645+
1646+
/** @type {AnotherGlobal.AnotherMethod} */
1647+
static #test4
1648+
}
1649+
`,
1650+
languageOptions: {
1651+
parser: typescriptEslintParser,
1652+
},
1653+
},
16271654
],
16281655
});

0 commit comments

Comments
 (0)