Skip to content

Typescript overrides confuses "require-jsdoc" rules #1369

Open
@mharj

Description

@mharj

Expected behavior

Have valid jsdoc block on function/method with override.

Actual behavior

On override actual implementation line eslint tells that jsdoc is missing.

ESLint Config

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import stylisticTs from '@stylistic/eslint-plugin-ts';
import importPlugin from 'eslint-plugin-import';
import prettierRecommended from 'eslint-plugin-prettier/recommended';
import sonarjs from 'eslint-plugin-sonarjs';
import tsParser from '@typescript-eslint/parser';
import cspellESLintPluginRecommended from '@cspell/eslint-plugin/recommended';
import jsdoc from 'eslint-plugin-jsdoc';

export default tseslint.config(
	eslint.configs.recommended,
	tseslint.configs.recommendedTypeChecked,
	tseslint.configs.stylisticTypeChecked,
	tseslint.configs.strictTypeChecked,
	importPlugin.flatConfigs.recommended,
	importPlugin.flatConfigs.typescript,
	sonarjs.configs.recommended,
	cspellESLintPluginRecommended,
	jsdoc.configs['flat/recommended-typescript'],
	prettierRecommended,
	{
		ignores: ['**/dist', '**/node_modules', '**/.github', '**/.nyc_output', '**/vite.config.mts', 'eslint.config.mjs'],
	},
	{
		plugins: {
			'@stylistic/ts': stylisticTs,
			jsdoc,
		},
		languageOptions: {
			parser: tsParser,
			ecmaVersion: 2020,
			sourceType: 'module',
			parserOptions: {
				project: './tsconfig.test.json',
			},
		},
		settings: {
			'import/resolver': {
				typescript: {
					extensions: ['.mts'],
					moduleDirectory: ['node_modules', 'src/'],
				},
			},
			jsdoc: {
				mode: 'typescript',
			},
		},
		rules: {
			'sort-imports': 'off',
			'import/order': [
				'warn',
				{
					groups: ['builtin', 'external', 'parent', 'sibling', 'index'],

					alphabetize: {
						order: 'asc',
						caseInsensitive: true,
					},

					named: true,
					'newlines-between': 'never',
				},
			],
			camelcase: 'off',
			'@typescript-eslint/naming-convention': [
				'warn',
				{
					selector: ['variable', 'parameter'],
					modifiers: ['destructured'],
					format: null,
				},
				{
					selector: 'variable',
					modifiers: ['const'],
					format: ['camelCase'],
					leadingUnderscore: 'allow',
				},
				{
					selector: 'variableLike',
					format: ['camelCase', 'PascalCase'],
					leadingUnderscore: 'allow',
				},
				{
					selector: 'typeAlias',
					format: ['PascalCase'],
				},
				{
					selector: 'interface',
					prefix: ['I'],
					format: ['PascalCase'],
				},
			],
			'import/no-useless-path-segments': 'warn',
			'import/no-duplicates': 'error',
			curly: 'error',
			'@typescript-eslint/no-this-alias': [
				'warn',
				{
					allowedNames: ['self'],
				},
			],
			'sort-keys': [
				'warn',
				'asc',
				{
					caseSensitive: false,
					natural: true,
					minKeys: 5,
				},
			],
			'no-unused-vars': 'off',
			'@typescript-eslint/no-unused-vars': [
				'warn',
				{
					argsIgnorePattern: '^_',
					varsIgnorePattern: '^_',
					caughtErrorsIgnorePattern: '^_',
				},
			],
			'@typescript-eslint/no-deprecated': 'warn',
			'lines-between-class-members': 'off',
			'@stylistic/ts/lines-between-class-members': [
				'warn',
				'always',
				{
					exceptAfterOverload: true,
					exceptAfterSingleLine: true,
				},
			],
			'@typescript-eslint/consistent-type-imports': [
				'warn',
				{
					prefer: 'type-imports',
					fixStyle: 'inline-type-imports',
				},
			],
			'@typescript-eslint/member-ordering': [
				'warn',
				{
					classes: ['static-field', 'static-method', 'field', 'constructor', 'public-method', 'protected-method', 'private-method', '#private-method'],
				},
			],
			'@typescript-eslint/no-unnecessary-condition': 'off',
			'sonarjs/use-type-alias': 'off',
			'@typescript-eslint/no-unsafe-function-type': 'off',
			'@typescript-eslint/no-explicit-any': 'off',
			'@typescript-eslint/consistent-indexed-object-style': 'off',
			'@typescript-eslint/array-type': 'off',
			'@typescript-eslint/consistent-type-definitions': 'off',
			'@typescript-eslint/no-unnecessary-type-parameters': 'off', // not working ok
			'jsdoc/no-types': 'off',
			'jsdoc/require-param-type': 'warn',
			'jsdoc/require-param': 'warn',
			'jsdoc/require-template': 'warn',
			'jsdoc/require-throws': 'warn',
			'jsdoc/require-returns': 'warn',
			'jsdoc/require-returns-type': 'warn',
			'jsdoc/check-values': 'error',
			'jsdoc/check-types': 'error',
			'jsdoc/no-restricted-syntax': [
				'warn',
				{
					contexts: [
						{
							comment: 'JsdocBlock:not(*:has(JsdocTag[tag=since]))',
							context: 'FunctionDeclaration',
							message: '@since required on each block',
						},
					],
				},
			],
		},
	},
	{
		files: ['**/*.test.mts', '**/*.test-d.mts'],
		rules: {
			'no-console': 'off',
			'no-proto': 'off',
			'sonarjs/no-duplicate-string': 'off',
			'sonarjs/assertions-in-tests': 'off',
			'@typescript-eslint/no-unsafe-argument': 'off',
			'@typescript-eslint/no-unsafe-assignment': 'off',
			'@typescript-eslint/no-explicit-any': 'off',
			'@typescript-eslint/no-confusing-void-expression': 'off',
			'@typescript-eslint/no-unsafe-member-access': 'off',
			'@typescript-eslint/no-empty-function': 'off',
		},
	},
);

ESLint sample

/**
 * Array map function with overload for NonEmptyArray
 * @example
 * const data = [{value: 'value'}] as const;
 * const result1: NonEmptyReadonlyArray<'value'> = arrayMap(data, (value) => value.value); // pick type from data
 * const result2: NonEmptyReadonlyArray<'value'> = arrayMap<'value', typeof data>(data, (value) => value.value); // enforce output type
 * @template Target - The type of the array to map to
 * @template Source - The type of the array to map from
 * @param {Source} data - The array to map
 * @param {MapCallback<Target, Source>} callback - Callback function to map data from the array
 * @returns {AnyArrayType<Target>} Mapped array
 * @since v0.2.0
 */
export function arrayMap<Target, Source extends NonEmptyArray<unknown> | NonEmptyReadonlyArray<unknown>>(
	data: Source,
	callback: MapCallback<Target, Source>,
): NonEmptyArray<Target>;
export function arrayMap<Target, Source extends Array<unknown>>(data: Source, callback: MapCallback<Target, Source>): Array<Target>;
export function arrayMap<Target, Source extends AnyArrayType>(data: Source, callback: MapCallback<Target, Source>): AnyArrayType<Target> {
	return data.map(callback);
}

or clone repo from https://github.com/luolapeikko/ts-common and "pnpm i && pnpm run lint"

Environment

  • Node version: v20.16.0
  • ESLint version v9.23.0
  • eslint-plugin-jsdoc version: 50.6.9

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions