Skip to content

Commit

Permalink
feat(require-template, check-template-names): add support `TSInte…
Browse files Browse the repository at this point in the history
…rfaceDeclaration`
  • Loading branch information
brettz9 committed Jul 30, 2024
1 parent 87a1270 commit 320a1eb
Show file tree
Hide file tree
Showing 8 changed files with 332 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .README/rules/check-template-names.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Checks that any `@template` names are actually used in the connected
`@typedef` or type alias.

Currently checks `TSTypeAliasDeclaration` such as:
Currently checks `TSInterfaceDeclaration` or `TSTypeAliasDeclaration` such as:

```ts
/**
Expand Down
2 changes: 1 addition & 1 deletion .README/rules/require-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Checks to see that `@template` tags are present for any detected type
parameters.

Currently checks `TSTypeAliasDeclaration` such as:
Currently checks `TSInterfaceDeclaration` or `TSTypeAliasDeclaration` such as:

```ts
export type Pairs<D, V> = [D, V | undefined];
Expand Down
50 changes: 49 additions & 1 deletion docs/rules/check-template-names.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Checks that any `@template` names are actually used in the connected
`@typedef` or type alias.

Currently checks `TSTypeAliasDeclaration` such as:
Currently checks `TSInterfaceDeclaration` or `TSTypeAliasDeclaration` such as:

```ts
/**
Expand Down Expand Up @@ -95,6 +95,33 @@ export type Extras<D, U> = [D, U | undefined];
* @property {V} foo
*/
// Message: @template D not in use

/**
* @template D
* @template V
*/
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
// Message: @template D not in use

/**
* @template D
* @template V
*/
export interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
// Message: @template D not in use

/**
* @template D
* @template V
*/
export default interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
// Message: @template D not in use
````


Expand Down Expand Up @@ -146,5 +173,26 @@ export type Extras<D, U, V> = [D, U, V | undefined];
* @property {D} foo
* @property {V} bar
*/

/**
* @template Type
*/
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}

/**
* @template Type
*/
export interface GenericIdentityFn<Type> {
(arg: Type): Type;
}

/**
* @template Type
*/
export default interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
````

47 changes: 46 additions & 1 deletion docs/rules/require-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Checks to see that `@template` tags are present for any detected type
parameters.

Currently checks `TSTypeAliasDeclaration` such as:
Currently checks `TSInterfaceDeclaration` or `TSTypeAliasDeclaration` such as:

```ts
export type Pairs<D, V> = [D, V | undefined];
Expand Down Expand Up @@ -114,6 +114,30 @@ export type Pairs<D, V> = [D, V | undefined];
* @property {X} bar
*/
// Message: Missing @template D

/**
*
*/
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
// Message: Missing @template Type

/**
*
*/
export interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
// Message: Missing @template Type

/**
*
*/
export default interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
// Message: Missing @template Type
````


Expand Down Expand Up @@ -164,5 +188,26 @@ export type Extras<D, U, V> = [D, U, V | undefined];
* @property {D} foo
* @property {V} bar
*/

/**
* @template Type
*/
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}

/**
* @template Type
*/
export interface GenericIdentityFn<Type> {
(arg: Type): Type;
}

/**
* @template Type
*/
export default interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
````

12 changes: 9 additions & 3 deletions src/rules/checkTemplateNames.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export default iterateJsdoc(({

const usedNames = new Set();
/**
* @param {import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
* @param {import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration|
* import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
*/
const checkParameters = (aliasDeclaration) => {
/* c8 ignore next -- Guard */
Expand All @@ -47,12 +48,17 @@ export default iterateJsdoc(({
return;
}
switch (nde.type) {
case 'ExportDefaultDeclaration':
case 'ExportNamedDeclaration':
if (nde.declaration?.type === 'TSTypeAliasDeclaration') {
checkParameters(nde.declaration);
switch (nde.declaration?.type) {
case 'TSTypeAliasDeclaration':
case 'TSInterfaceDeclaration':
checkParameters(nde.declaration);
break;
}
break;
case 'TSTypeAliasDeclaration':
case 'TSInterfaceDeclaration':
checkParameters(nde);
break;
}
Expand Down
26 changes: 19 additions & 7 deletions src/rules/requireTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ export default iterateJsdoc(({
}

/**
* @param {import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
* @param {import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration|
* import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
*/
const checkParameters = (aliasDeclaration) => {
const checkTypeParams = (aliasDeclaration) => {
/* c8 ignore next -- Guard */
const {params} = aliasDeclaration.typeParameters ?? {params: []};
for (const {name: {name}} of params) {
Expand All @@ -50,28 +51,39 @@ export default iterateJsdoc(({
}
};

const handleTypeAliases = () => {
const handleTypes = () => {
const nde = /** @type {import('@typescript-eslint/types').TSESTree.Node} */ (
node
);
if (!nde) {
return;
}
switch (nde.type) {
case 'ExportDefaultDeclaration':
switch (nde.declaration?.type) {
case 'TSInterfaceDeclaration':
checkTypeParams(nde.declaration);
break;
}
break;
case 'ExportNamedDeclaration':
if (nde.declaration?.type === 'TSTypeAliasDeclaration') {
checkParameters(nde.declaration);
switch (nde.declaration?.type) {
case 'TSTypeAliasDeclaration':
case 'TSInterfaceDeclaration':
checkTypeParams(nde.declaration);
break;
}
break;
case 'TSTypeAliasDeclaration':
checkParameters(nde);
case 'TSInterfaceDeclaration':
checkTypeParams(nde);
break;
}
};

const typedefTags = utils.getTags('typedef');
if (!typedefTags.length || typedefTags.length >= 2) {
handleTypeAliases();
handleTypes();
return;
}

Expand Down
111 changes: 111 additions & 0 deletions test/rules/assertions/checkTemplateNames.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,78 @@ export default {
},
],
},
{
code: `
/**
* @template D
* @template V
*/
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
`,
errors: [
{
line: 3,
message: '@template D not in use',
},
{
line: 4,
message: '@template V not in use',
},
],
languageOptions: {
parser: typescriptEslintParser
},
},
{
code: `
/**
* @template D
* @template V
*/
export interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
`,
errors: [
{
line: 3,
message: '@template D not in use',
},
{
line: 4,
message: '@template V not in use',
},
],
languageOptions: {
parser: typescriptEslintParser
},
},
{
code: `
/**
* @template D
* @template V
*/
export default interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
`,
errors: [
{
line: 3,
message: '@template D not in use',
},
{
line: 4,
message: '@template V not in use',
},
],
languageOptions: {
parser: typescriptEslintParser
},
},
],
valid: [
{
Expand Down Expand Up @@ -228,5 +300,44 @@ export default {
*/
`,
},
{
code: `
/**
* @template Type
*/
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
`,
languageOptions: {
parser: typescriptEslintParser
},
},
{
code: `
/**
* @template Type
*/
export interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
`,
languageOptions: {
parser: typescriptEslintParser
},
},
{
code: `
/**
* @template Type
*/
export default interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
`,
languageOptions: {
parser: typescriptEslintParser
},
},
],
};
Loading

0 comments on commit 320a1eb

Please sign in to comment.