diff --git a/packages/eslint-plugin/docs/rules/require-types-exports.mdx b/packages/eslint-plugin/docs/rules/require-types-exports.mdx
new file mode 100644
index 000000000000..baa939fc2115
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/require-types-exports.mdx
@@ -0,0 +1,80 @@
+---
+description: 'Require exporting types that are used in exported entities.'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+> 🛑 This file is source code, not the primary documentation location! 🛑
+>
+> See **https://typescript-eslint.io/rules/require-types-exports** for documentation.
+
+When exporting entities from a module, it is recommended to export also all the
+types that are used in their declarations. This is useful for consumers of the
+module, as it allows them to use the types in their own code without having to
+use utility types like [`Parameters`](https://www.typescriptlang.org/docs/handbook/utility-types.html#parameterstype)
+or [`ReturnType`](https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype)
+in order to extract the types from your code.
+
+## Examples
+
+
+
+
+```ts
+type Arg = string;
+type Result = number;
+
+export function strLength(arg: Arg): Result {
+ return arg.length;
+}
+
+interface Fruit {
+ name: string;
+ color: string;
+}
+
+export const getFruitName = (fruit: Fruit) => fruit.name;
+
+enum Color {
+ Red = 'red',
+ Green = 'green',
+ Blue = 'blue',
+}
+
+export declare function getRandomColor(): Color;
+```
+
+
+
+
+```ts
+export type Arg = string;
+export type Result = number;
+
+export function strLength(arg: Arg): Result {
+ return arg.length;
+}
+
+export interface Fruit {
+ name: string;
+ color: string;
+}
+
+export const getFruitName = (fruit: Fruit) => fruit.name;
+
+export enum Color {
+ Red = 'red',
+ Green = 'green',
+ Blue = 'blue',
+}
+
+export declare function getRandomColor(): Color;
+```
+
+
+
+
+## When Not To Use It
+
+When you don't want to enforce exporting types that are used in exported functions declarations.
diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts
index c8973db2dd8b..9e8de4b1c1f2 100644
--- a/packages/eslint-plugin/src/configs/all.ts
+++ b/packages/eslint-plugin/src/configs/all.ts
@@ -146,6 +146,7 @@ export = {
'@typescript-eslint/require-array-sort-compare': 'error',
'require-await': 'off',
'@typescript-eslint/require-await': 'error',
+ '@typescript-eslint/require-types-exports': 'error',
'@typescript-eslint/restrict-plus-operands': 'error',
'@typescript-eslint/restrict-template-expressions': 'error',
'no-return-await': 'off',
diff --git a/packages/eslint-plugin/src/configs/strict-type-checked.ts b/packages/eslint-plugin/src/configs/strict-type-checked.ts
index cdce2e4962b6..cc55b1ab5c10 100644
--- a/packages/eslint-plugin/src/configs/strict-type-checked.ts
+++ b/packages/eslint-plugin/src/configs/strict-type-checked.ts
@@ -73,6 +73,7 @@ export = {
'@typescript-eslint/prefer-return-this-type': 'error',
'require-await': 'off',
'@typescript-eslint/require-await': 'error',
+ '@typescript-eslint/require-types-exports': 'error',
'@typescript-eslint/restrict-plus-operands': [
'error',
{
diff --git a/packages/eslint-plugin/src/configs/strict.ts b/packages/eslint-plugin/src/configs/strict.ts
index 9c51d5c47348..cb825c95d3cb 100644
--- a/packages/eslint-plugin/src/configs/strict.ts
+++ b/packages/eslint-plugin/src/configs/strict.ts
@@ -40,6 +40,7 @@ export = {
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/prefer-literal-enum-member': 'error',
+ '@typescript-eslint/require-types-exports': 'error',
'@typescript-eslint/triple-slash-reference': 'error',
'@typescript-eslint/unified-signatures': 'error',
},
diff --git a/packages/eslint-plugin/src/rules/array-type.ts b/packages/eslint-plugin/src/rules/array-type.ts
index 26b5c270914e..ebc32526ee89 100644
--- a/packages/eslint-plugin/src/rules/array-type.ts
+++ b/packages/eslint-plugin/src/rules/array-type.ts
@@ -72,13 +72,13 @@ function typeNeedsParentheses(node: TSESTree.Node): boolean {
}
export type OptionString = 'array-simple' | 'array' | 'generic';
-type Options = [
+export type Options = [
{
default: OptionString;
readonly?: OptionString;
},
];
-type MessageIds =
+export type MessageIds =
| 'errorStringArray'
| 'errorStringArraySimple'
| 'errorStringGeneric'
diff --git a/packages/eslint-plugin/src/rules/ban-ts-comment.ts b/packages/eslint-plugin/src/rules/ban-ts-comment.ts
index b554510f57d3..71ec5696f867 100644
--- a/packages/eslint-plugin/src/rules/ban-ts-comment.ts
+++ b/packages/eslint-plugin/src/rules/ban-ts-comment.ts
@@ -3,12 +3,12 @@ import { AST_TOKEN_TYPES } from '@typescript-eslint/utils';
import { createRule, getStringLength, nullThrows } from '../util';
-type DirectiveConfig =
+export type DirectiveConfig =
| boolean
| 'allow-with-description'
| { descriptionFormat: string };
-interface Options {
+export interface Options {
'ts-expect-error'?: DirectiveConfig;
'ts-ignore'?: DirectiveConfig;
'ts-nocheck'?: DirectiveConfig;
@@ -18,7 +18,7 @@ interface Options {
const defaultMinimumDescriptionLength = 3;
-type MessageIds =
+export type MessageIds =
| 'tsDirectiveComment'
| 'tsIgnoreInsteadOfExpectError'
| 'tsDirectiveCommentDescriptionNotMatchPattern'
diff --git a/packages/eslint-plugin/src/rules/class-literal-property-style.ts b/packages/eslint-plugin/src/rules/class-literal-property-style.ts
index 6813e80f60d8..d8aa8d124fe5 100644
--- a/packages/eslint-plugin/src/rules/class-literal-property-style.ts
+++ b/packages/eslint-plugin/src/rules/class-literal-property-style.ts
@@ -9,8 +9,8 @@ import {
nullThrows,
} from '../util';
-type Options = ['fields' | 'getters'];
-type MessageIds =
+export type Options = ['fields' | 'getters'];
+export type MessageIds =
| 'preferFieldStyle'
| 'preferFieldStyleSuggestion'
| 'preferGetterStyle'
diff --git a/packages/eslint-plugin/src/rules/class-methods-use-this.ts b/packages/eslint-plugin/src/rules/class-methods-use-this.ts
index 6236a46ddb7b..793c8e8a5d90 100644
--- a/packages/eslint-plugin/src/rules/class-methods-use-this.ts
+++ b/packages/eslint-plugin/src/rules/class-methods-use-this.ts
@@ -8,7 +8,7 @@ import {
getStaticStringValue,
} from '../util';
-type Options = [
+export type Options = [
{
exceptMethods?: string[];
enforceForClassFields?: boolean;
@@ -16,7 +16,7 @@ type Options = [
ignoreClassesThatImplementAnInterface?: boolean | 'public-fields';
},
];
-type MessageIds = 'missingThis';
+export type MessageIds = 'missingThis';
export default createRule({
name: 'class-methods-use-this',
diff --git a/packages/eslint-plugin/src/rules/comma-spacing.ts b/packages/eslint-plugin/src/rules/comma-spacing.ts
index cd283e4c97ac..79e9450c012b 100644
--- a/packages/eslint-plugin/src/rules/comma-spacing.ts
+++ b/packages/eslint-plugin/src/rules/comma-spacing.ts
@@ -10,13 +10,13 @@ import {
isTokenOnSameLine,
} from '../util';
-type Options = [
+export type Options = [
{
before: boolean;
after: boolean;
},
];
-type MessageIds = 'missing' | 'unexpected';
+export type MessageIds = 'missing' | 'unexpected';
export default createRule({
name: 'comma-spacing',
diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts
index 166a2a8ecfda..466fc83a544a 100644
--- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts
+++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts
@@ -3,8 +3,8 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule, nullThrows, NullThrowsReasons } from '../util';
-type MessageIds = 'preferConstructor' | 'preferTypeAnnotation';
-type Options = ['constructor' | 'type-annotation'];
+export type MessageIds = 'preferConstructor' | 'preferTypeAnnotation';
+export type Options = ['constructor' | 'type-annotation'];
export default createRule({
name: 'consistent-generic-constructors',
diff --git a/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts b/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts
index f0f91cc32b8e..c85f49f71bf3 100644
--- a/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts
+++ b/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts
@@ -3,8 +3,8 @@ import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
import { createRule } from '../util';
-type MessageIds = 'preferIndexSignature' | 'preferRecord';
-type Options = ['index-signature' | 'record'];
+export type MessageIds = 'preferIndexSignature' | 'preferRecord';
+export type Options = ['index-signature' | 'record'];
export default createRule({
name: 'consistent-indexed-object-style',
diff --git a/packages/eslint-plugin/src/rules/consistent-return.ts b/packages/eslint-plugin/src/rules/consistent-return.ts
index 5d4cc3fb9256..8abb899ef7ce 100644
--- a/packages/eslint-plugin/src/rules/consistent-return.ts
+++ b/packages/eslint-plugin/src/rules/consistent-return.ts
@@ -11,10 +11,10 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('consistent-return');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
-type FunctionNode =
+export type FunctionNode =
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression
| TSESTree.ArrowFunctionExpression;
diff --git a/packages/eslint-plugin/src/rules/consistent-type-exports.ts b/packages/eslint-plugin/src/rules/consistent-type-exports.ts
index 236659d13adb..cbd3b3236303 100644
--- a/packages/eslint-plugin/src/rules/consistent-type-exports.ts
+++ b/packages/eslint-plugin/src/rules/consistent-type-exports.ts
@@ -12,7 +12,7 @@ import {
NullThrowsReasons,
} from '../util';
-type Options = [
+export type Options = [
{
fixMixedExportsWithInlineTypeSpecifier: boolean;
},
@@ -32,7 +32,7 @@ interface ReportValueExport {
inlineTypeSpecifiers: TSESTree.ExportSpecifier[];
}
-type MessageIds =
+export type MessageIds =
| 'multipleExportsAreTypes'
| 'singleExportIsType'
| 'typeOverValue';
diff --git a/packages/eslint-plugin/src/rules/consistent-type-imports.ts b/packages/eslint-plugin/src/rules/consistent-type-imports.ts
index f4e98d2d9e00..3dfcb8ced4e0 100644
--- a/packages/eslint-plugin/src/rules/consistent-type-imports.ts
+++ b/packages/eslint-plugin/src/rules/consistent-type-imports.ts
@@ -15,10 +15,10 @@ import {
NullThrowsReasons,
} from '../util';
-type Prefer = 'no-type-imports' | 'type-imports';
-type FixStyle = 'inline-type-imports' | 'separate-type-imports';
+export type Prefer = 'no-type-imports' | 'type-imports';
+export type FixStyle = 'inline-type-imports' | 'separate-type-imports';
-type Options = [
+export type Options = [
{
prefer?: Prefer;
disallowTypeAnnotations?: boolean;
@@ -44,7 +44,7 @@ interface ReportValueImport {
inlineTypeSpecifiers: TSESTree.ImportSpecifier[];
}
-type MessageIds =
+export type MessageIds =
| 'typeOverValue'
| 'someImportsAreOnlyTypes'
| 'avoidImportType'
diff --git a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts
index 9904dc2e5336..54e8c132c53d 100644
--- a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts
+++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts
@@ -9,7 +9,7 @@ import {
isValidFunctionExpressionReturnType,
} from '../util/explicitReturnTypeUtils';
-type Options = [
+export type Options = [
{
allowExpressions?: boolean;
allowTypedFunctionExpressions?: boolean;
@@ -21,7 +21,7 @@ type Options = [
allowIIFEs?: boolean;
},
];
-type MessageIds = 'missingReturnType';
+export type MessageIds = 'missingReturnType';
type FunctionNode =
| TSESTree.ArrowFunctionExpression
diff --git a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts
index 5947d292f8bd..2a885d275d37 100644
--- a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts
+++ b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts
@@ -12,12 +12,12 @@ import {
getParameterPropertyHeadLoc,
} from '../util/getMemberHeadLoc';
-type AccessibilityLevel =
+export type AccessibilityLevel =
| 'explicit' // require an accessor (including public)
| 'no-public' // don't require public
| 'off'; // don't check
-interface Config {
+export interface Config {
accessibility?: AccessibilityLevel;
ignoredMethodNames?: string[];
overrides?: {
@@ -29,9 +29,9 @@ interface Config {
};
}
-type Options = [Config];
+export type Options = [Config];
-type MessageIds =
+export type MessageIds =
| 'addExplicitAccessibility'
| 'missingAccessibility'
| 'unwantedPublicAccessibility';
diff --git a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts
index c8981e403941..66e606d20538 100644
--- a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts
+++ b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts
@@ -16,7 +16,7 @@ import {
isTypedFunctionExpression,
} from '../util/explicitReturnTypeUtils';
-type Options = [
+export type Options = [
{
allowArgumentsExplicitlyTypedAsAny?: boolean;
allowDirectConstAssertionInArrowFunctions?: boolean;
@@ -25,7 +25,7 @@ type Options = [
allowTypedFunctionExpressions?: boolean;
},
];
-type MessageIds =
+export type MessageIds =
| 'anyTypedArg'
| 'anyTypedArgUnnamed'
| 'missingArgType'
diff --git a/packages/eslint-plugin/src/rules/indent.ts b/packages/eslint-plugin/src/rules/indent.ts
index 248ecd0d7e8a..11a5d8313a7e 100644
--- a/packages/eslint-plugin/src/rules/indent.ts
+++ b/packages/eslint-plugin/src/rules/indent.ts
@@ -17,8 +17,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('indent');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
const KNOWN_NODES = new Set([
// Class properties aren't yet supported by eslint...
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index dfafc8a8eff5..e90a726d7c49 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -131,6 +131,7 @@ import promiseFunctionAsync from './promise-function-async';
import quotes from './quotes';
import requireArraySortCompare from './require-array-sort-compare';
import requireAwait from './require-await';
+import requireTypesExports from './require-types-exports';
import restrictPlusOperands from './restrict-plus-operands';
import restrictTemplateExpressions from './restrict-template-expressions';
import returnAwait from './return-await';
@@ -281,6 +282,7 @@ export default {
quotes: quotes,
'require-array-sort-compare': requireArraySortCompare,
'require-await': requireAwait,
+ 'require-types-exports': requireTypesExports,
'restrict-plus-operands': restrictPlusOperands,
'restrict-template-expressions': restrictTemplateExpressions,
'return-await': returnAwait,
diff --git a/packages/eslint-plugin/src/rules/lines-between-class-members.ts b/packages/eslint-plugin/src/rules/lines-between-class-members.ts
index 60da9308757a..2304f7ac405b 100644
--- a/packages/eslint-plugin/src/rules/lines-between-class-members.ts
+++ b/packages/eslint-plugin/src/rules/lines-between-class-members.ts
@@ -11,8 +11,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('lines-between-class-members');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
const schema = Object.values(
deepMerge(
diff --git a/packages/eslint-plugin/src/rules/member-delimiter-style.ts b/packages/eslint-plugin/src/rules/member-delimiter-style.ts
index 428f31f3667e..13a398defdf7 100644
--- a/packages/eslint-plugin/src/rules/member-delimiter-style.ts
+++ b/packages/eslint-plugin/src/rules/member-delimiter-style.ts
@@ -4,10 +4,10 @@ import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema';
import { createRule, deepMerge } from '../util';
-type Delimiter = 'comma' | 'none' | 'semi';
+export type Delimiter = 'comma' | 'none' | 'semi';
// need type's implicit index sig for deepMerge
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
-type TypeOptions = {
+export type TypeOptions = {
delimiter?: Delimiter;
requireLast?: boolean;
};
@@ -15,19 +15,19 @@ type TypeOptionsWithType = TypeOptions & {
type: string;
};
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
-type BaseOptions = {
+export type BaseOptions = {
multiline?: TypeOptions;
singleline?: TypeOptions;
};
-type Config = BaseOptions & {
+export type Config = BaseOptions & {
overrides?: {
typeLiteral?: BaseOptions;
interface?: BaseOptions;
};
multilineDetection?: 'brackets' | 'last-member';
};
-type Options = [Config];
-type MessageIds =
+export type Options = [Config];
+export type MessageIds =
| 'expectedComma'
| 'expectedSemi'
| 'unexpectedComma'
diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts
index 4713192770a7..c357095ae988 100644
--- a/packages/eslint-plugin/src/rules/member-ordering.ts
+++ b/packages/eslint-plugin/src/rules/member-ordering.ts
@@ -17,9 +17,9 @@ export type MessageIds =
| 'incorrectOrder'
| 'incorrectRequiredMembersOrder';
-type ReadonlyType = 'readonly-field' | 'readonly-signature';
+export type ReadonlyType = 'readonly-field' | 'readonly-signature';
-type MemberKind =
+export type MemberKind =
| ReadonlyType
| 'accessor'
| 'call-signature'
@@ -31,7 +31,7 @@ type MemberKind =
| 'signature'
| 'static-initialization';
-type DecoratedMemberKind =
+export type DecoratedMemberKind =
| Exclude
| 'accessor'
| 'field'
@@ -39,16 +39,16 @@ type DecoratedMemberKind =
| 'method'
| 'set';
-type NonCallableMemberKind = Exclude<
+export type NonCallableMemberKind = Exclude<
MemberKind,
'constructor' | 'readonly-signature' | 'signature'
>;
-type MemberScope = 'abstract' | 'instance' | 'static';
+export type MemberScope = 'abstract' | 'instance' | 'static';
-type Accessibility = TSESTree.Accessibility | '#private';
+export type Accessibility = TSESTree.Accessibility | '#private';
-type BaseMemberType =
+export type BaseMemberType =
| MemberKind
| `${Accessibility}-${Exclude<
MemberKind,
@@ -59,26 +59,26 @@ type BaseMemberType =
| `${MemberScope}-${NonCallableMemberKind}`
| `decorated-${DecoratedMemberKind}`;
-type MemberType = BaseMemberType | BaseMemberType[];
+export type MemberType = BaseMemberType | BaseMemberType[];
-type AlphabeticalOrder =
+export type AlphabeticalOrder =
| 'alphabetically-case-insensitive'
| 'alphabetically'
| 'natural-case-insensitive'
| 'natural';
-type Order = AlphabeticalOrder | 'as-written';
+export type Order = AlphabeticalOrder | 'as-written';
-interface SortedOrderConfig {
+export interface SortedOrderConfig {
memberTypes?: MemberType[] | 'never';
optionalityOrder?: OptionalityOrder;
order?: Order;
}
-type OrderConfig = MemberType[] | SortedOrderConfig | 'never';
-type Member = TSESTree.ClassElement | TSESTree.TypeElement;
+export type OrderConfig = MemberType[] | SortedOrderConfig | 'never';
+export type Member = TSESTree.ClassElement | TSESTree.TypeElement;
-type OptionalityOrder = 'optional-first' | 'required-first';
+export type OptionalityOrder = 'optional-first' | 'required-first';
export type Options = [
{
diff --git a/packages/eslint-plugin/src/rules/no-array-delete.ts b/packages/eslint-plugin/src/rules/no-array-delete.ts
index d9900a1df10f..96bd2fdc2568 100644
--- a/packages/eslint-plugin/src/rules/no-array-delete.ts
+++ b/packages/eslint-plugin/src/rules/no-array-delete.ts
@@ -8,7 +8,7 @@ import {
getParserServices,
} from '../util';
-type MessageId = 'noArrayDelete' | 'useSplice';
+export type MessageId = 'noArrayDelete' | 'useSplice';
export default createRule<[], MessageId>({
name: 'no-array-delete',
diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts
index 0369e66fe66f..0dee231a011f 100644
--- a/packages/eslint-plugin/src/rules/no-base-to-string.ts
+++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts
@@ -10,12 +10,12 @@ enum Usefulness {
Sometimes = 'may',
}
-type Options = [
+export type Options = [
{
ignoredTypeNames?: string[];
},
];
-type MessageIds = 'baseToString';
+export type MessageIds = 'baseToString';
export default createRule({
name: 'no-base-to-string',
diff --git a/packages/eslint-plugin/src/rules/no-dupe-class-members.ts b/packages/eslint-plugin/src/rules/no-dupe-class-members.ts
index 08dd0b35d3b8..e1ffecdd6dad 100644
--- a/packages/eslint-plugin/src/rules/no-dupe-class-members.ts
+++ b/packages/eslint-plugin/src/rules/no-dupe-class-members.ts
@@ -10,8 +10,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-dupe-class-members');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
export default createRule({
name: 'no-dupe-class-members',
diff --git a/packages/eslint-plugin/src/rules/no-empty-function.ts b/packages/eslint-plugin/src/rules/no-empty-function.ts
index 6a8e90ebaf13..79e6ffd9cc56 100644
--- a/packages/eslint-plugin/src/rules/no-empty-function.ts
+++ b/packages/eslint-plugin/src/rules/no-empty-function.ts
@@ -11,8 +11,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-empty-function');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
const schema = deepMerge(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- https://github.com/microsoft/TypeScript/issues/17002
diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts
index 674b86d44bf9..0e56604d2567 100644
--- a/packages/eslint-plugin/src/rules/no-empty-interface.ts
+++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts
@@ -4,12 +4,12 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule, isDefinitionFile } from '../util';
-type Options = [
+export type Options = [
{
allowSingleExtends?: boolean;
},
];
-type MessageIds = 'noEmpty' | 'noEmptyWithSuper';
+export type MessageIds = 'noEmpty' | 'noEmptyWithSuper';
export default createRule({
name: 'no-empty-interface',
diff --git a/packages/eslint-plugin/src/rules/no-extra-parens.ts b/packages/eslint-plugin/src/rules/no-extra-parens.ts
index 0ed0f4c6b4de..bd1dde121c3f 100644
--- a/packages/eslint-plugin/src/rules/no-extra-parens.ts
+++ b/packages/eslint-plugin/src/rules/no-extra-parens.ts
@@ -13,8 +13,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-extra-parens');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
export default createRule({
name: 'no-extra-parens',
diff --git a/packages/eslint-plugin/src/rules/no-extra-semi.ts b/packages/eslint-plugin/src/rules/no-extra-semi.ts
index 4d68f2d6db46..d384cde41fc8 100644
--- a/packages/eslint-plugin/src/rules/no-extra-semi.ts
+++ b/packages/eslint-plugin/src/rules/no-extra-semi.ts
@@ -7,8 +7,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-extra-semi');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
export default createRule({
name: 'no-extra-semi',
diff --git a/packages/eslint-plugin/src/rules/no-extraneous-class.ts b/packages/eslint-plugin/src/rules/no-extraneous-class.ts
index a7c05f8cc5a8..6d975a454b9a 100644
--- a/packages/eslint-plugin/src/rules/no-extraneous-class.ts
+++ b/packages/eslint-plugin/src/rules/no-extraneous-class.ts
@@ -3,7 +3,7 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule } from '../util';
-type Options = [
+export type Options = [
{
allowConstructorOnly?: boolean;
allowEmpty?: boolean;
@@ -11,7 +11,7 @@ type Options = [
allowWithDecorator?: boolean;
},
];
-type MessageIds = 'empty' | 'onlyConstructor' | 'onlyStatic';
+export type MessageIds = 'empty' | 'onlyConstructor' | 'onlyStatic';
export default createRule({
name: 'no-extraneous-class',
diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts
index b49516184317..f7b3f36ab506 100644
--- a/packages/eslint-plugin/src/rules/no-floating-promises.ts
+++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts
@@ -15,7 +15,7 @@ import {
typeMatchesSpecifier,
} from '../util';
-type Options = [
+export type Options = [
{
allowForKnownSafePromises?: TypeOrValueSpecifier[];
checkThenables?: boolean;
@@ -24,7 +24,7 @@ type Options = [
},
];
-type MessageId =
+export type MessageId =
| 'floating'
| 'floatingVoid'
| 'floatingUselessRejectionHandler'
diff --git a/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts b/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts
index 1658d471bc7b..585e78a177de 100644
--- a/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts
+++ b/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts
@@ -9,8 +9,8 @@ import {
NullThrowsReasons,
} from '../util';
-type Options = [];
-type MessageIds = 'useTopLevelQualifier';
+export type Options = [];
+export type MessageIds = 'useTopLevelQualifier';
export default createRule({
name: 'no-import-type-side-effects',
diff --git a/packages/eslint-plugin/src/rules/no-inferrable-types.ts b/packages/eslint-plugin/src/rules/no-inferrable-types.ts
index 51ead2ae476a..3b76ee94b0c6 100644
--- a/packages/eslint-plugin/src/rules/no-inferrable-types.ts
+++ b/packages/eslint-plugin/src/rules/no-inferrable-types.ts
@@ -4,13 +4,13 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule, nullThrows, NullThrowsReasons } from '../util';
-type Options = [
+export type Options = [
{
ignoreParameters?: boolean;
ignoreProperties?: boolean;
},
];
-type MessageIds = 'noInferrableType';
+export type MessageIds = 'noInferrableType';
export default createRule({
name: 'no-inferrable-types',
diff --git a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts
index 8ae1604a210b..4d4189ac7dfc 100644
--- a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts
+++ b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts
@@ -3,12 +3,12 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule } from '../util';
-interface Options {
+export interface Options {
allowInGenericTypeArguments?: [string, ...string[]] | boolean;
allowAsThisParameter?: boolean;
}
-type MessageIds =
+export type MessageIds =
| 'invalidVoidForGeneric'
| 'invalidVoidNotReturn'
| 'invalidVoidNotReturnOrGeneric'
diff --git a/packages/eslint-plugin/src/rules/no-loop-func.ts b/packages/eslint-plugin/src/rules/no-loop-func.ts
index a34217453b0d..096e3618f5d4 100644
--- a/packages/eslint-plugin/src/rules/no-loop-func.ts
+++ b/packages/eslint-plugin/src/rules/no-loop-func.ts
@@ -10,8 +10,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-loop-func');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
export default createRule({
name: 'no-loop-func',
diff --git a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts
index 06938f9e4dcf..51d2bdee9f25 100644
--- a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts
+++ b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts
@@ -9,8 +9,10 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-loss-of-precision');
-type Options = InferOptionsTypeFromRule>;
-type MessageIds = InferMessageIdsTypeFromRule>;
+export type Options = InferOptionsTypeFromRule>;
+export type MessageIds = InferMessageIdsTypeFromRule<
+ NonNullable
+>;
export default createRule({
name: 'no-loss-of-precision',
diff --git a/packages/eslint-plugin/src/rules/no-magic-numbers.ts b/packages/eslint-plugin/src/rules/no-magic-numbers.ts
index 5a86056cb90d..d31cdd895e36 100644
--- a/packages/eslint-plugin/src/rules/no-magic-numbers.ts
+++ b/packages/eslint-plugin/src/rules/no-magic-numbers.ts
@@ -11,8 +11,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-magic-numbers');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
// Extend base schema with additional property to ignore TS numeric literal types
const schema = deepMerge(
diff --git a/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts b/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts
index e8fdde8d1f33..d1d21612476c 100644
--- a/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts
+++ b/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts
@@ -5,7 +5,7 @@ import * as ts from 'typescript';
import { createRule } from '../util';
-type Options = [
+export type Options = [
{
checkNever: boolean;
},
diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts
index 1a58d884dc7c..2c386402a162 100644
--- a/packages/eslint-plugin/src/rules/no-misused-promises.ts
+++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts
@@ -9,7 +9,7 @@ import {
isRestParameterDeclaration,
} from '../util';
-type Options = [
+export type Options = [
{
checksConditionals?: boolean;
checksVoidReturn?: ChecksVoidReturnOptions | boolean;
@@ -17,7 +17,7 @@ type Options = [
},
];
-interface ChecksVoidReturnOptions {
+export interface ChecksVoidReturnOptions {
arguments?: boolean;
attributes?: boolean;
properties?: boolean;
@@ -25,7 +25,7 @@ interface ChecksVoidReturnOptions {
variables?: boolean;
}
-type MessageId =
+export type MessageId =
| 'conditional'
| 'spread'
| 'voidReturnArgument'
diff --git a/packages/eslint-plugin/src/rules/no-namespace.ts b/packages/eslint-plugin/src/rules/no-namespace.ts
index c6b9213259be..67a4979358ba 100644
--- a/packages/eslint-plugin/src/rules/no-namespace.ts
+++ b/packages/eslint-plugin/src/rules/no-namespace.ts
@@ -3,13 +3,13 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule, isDefinitionFile } from '../util';
-type Options = [
+export type Options = [
{
allowDeclarations?: boolean;
allowDefinitionFiles?: boolean;
},
];
-type MessageIds = 'moduleSyntaxIsPreferred';
+export type MessageIds = 'moduleSyntaxIsPreferred';
export default createRule({
name: 'no-namespace',
diff --git a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts
index 8545b0e1c110..f34637eeb42d 100644
--- a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts
+++ b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts
@@ -8,7 +8,7 @@ import {
NullThrowsReasons,
} from '../util';
-type MessageIds = 'noNonNull' | 'suggestOptionalChain';
+export type MessageIds = 'noNonNull' | 'suggestOptionalChain';
export default createRule<[], MessageIds>({
name: 'no-non-null-assertion',
diff --git a/packages/eslint-plugin/src/rules/no-redeclare.ts b/packages/eslint-plugin/src/rules/no-redeclare.ts
index b084d90650b0..782df71aadbc 100644
--- a/packages/eslint-plugin/src/rules/no-redeclare.ts
+++ b/packages/eslint-plugin/src/rules/no-redeclare.ts
@@ -4,8 +4,11 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule, getNameLocationInGlobalDirectiveComment } from '../util';
-type MessageIds = 'redeclared' | 'redeclaredAsBuiltin' | 'redeclaredBySyntax';
-type Options = [
+export type MessageIds =
+ | 'redeclared'
+ | 'redeclaredAsBuiltin'
+ | 'redeclaredBySyntax';
+export type Options = [
{
builtinGlobals?: boolean;
ignoreDeclarationMerge?: boolean;
diff --git a/packages/eslint-plugin/src/rules/no-require-imports.ts b/packages/eslint-plugin/src/rules/no-require-imports.ts
index 1956694484f4..577b36371905 100644
--- a/packages/eslint-plugin/src/rules/no-require-imports.ts
+++ b/packages/eslint-plugin/src/rules/no-require-imports.ts
@@ -3,12 +3,12 @@ import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
import * as util from '../util';
-type Options = [
+export type Options = [
{
allow: string[];
},
];
-type MessageIds = 'noRequireImports';
+export type MessageIds = 'noRequireImports';
export default util.createRule({
name: 'no-require-imports',
diff --git a/packages/eslint-plugin/src/rules/no-shadow.ts b/packages/eslint-plugin/src/rules/no-shadow.ts
index f66c21f6cdb1..78b59c53c775 100644
--- a/packages/eslint-plugin/src/rules/no-shadow.ts
+++ b/packages/eslint-plugin/src/rules/no-shadow.ts
@@ -8,8 +8,8 @@ import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
import { createRule } from '../util';
-type MessageIds = 'noShadow' | 'noShadowGlobal';
-type Options = [
+export type MessageIds = 'noShadow' | 'noShadowGlobal';
+export type Options = [
{
allow?: string[];
builtinGlobals?: boolean;
diff --git a/packages/eslint-plugin/src/rules/no-this-alias.ts b/packages/eslint-plugin/src/rules/no-this-alias.ts
index 98006e56328d..5ddbc7463bf0 100644
--- a/packages/eslint-plugin/src/rules/no-this-alias.ts
+++ b/packages/eslint-plugin/src/rules/no-this-alias.ts
@@ -3,13 +3,13 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule } from '../util';
-type Options = [
+export type Options = [
{
allowDestructuring?: boolean;
allowedNames?: string[];
},
];
-type MessageIds = 'thisAssignment' | 'thisDestructure';
+export type MessageIds = 'thisAssignment' | 'thisDestructure';
export default createRule({
name: 'no-this-alias',
diff --git a/packages/eslint-plugin/src/rules/no-throw-literal.ts b/packages/eslint-plugin/src/rules/no-throw-literal.ts
index d893dbc164a5..2318c75b742e 100644
--- a/packages/eslint-plugin/src/rules/no-throw-literal.ts
+++ b/packages/eslint-plugin/src/rules/no-throw-literal.ts
@@ -10,9 +10,9 @@ import {
isTypeUnknownType,
} from '../util';
-type MessageIds = 'object' | 'undef';
+export type MessageIds = 'object' | 'undef';
-type Options = [
+export type Options = [
{
allowThrowingAny?: boolean;
allowThrowingUnknown?: boolean;
diff --git a/packages/eslint-plugin/src/rules/no-type-alias.ts b/packages/eslint-plugin/src/rules/no-type-alias.ts
index 53e64a3b5c69..cd7898f7e9c4 100644
--- a/packages/eslint-plugin/src/rules/no-type-alias.ts
+++ b/packages/eslint-plugin/src/rules/no-type-alias.ts
@@ -3,14 +3,14 @@ import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils';
import { createRule } from '../util';
-type Values =
+export type Values =
| 'always'
| 'in-intersections'
| 'in-unions-and-intersections'
| 'in-unions'
| 'never';
-type Options = [
+export type Options = [
{
allowAliases?: Values;
allowCallbacks?: 'always' | 'never';
@@ -22,7 +22,7 @@ type Options = [
allowGenerics?: 'always' | 'never';
},
];
-type MessageIds = 'noCompositionAlias' | 'noTypeAlias';
+export type MessageIds = 'noCompositionAlias' | 'noTypeAlias';
type CompositionType =
| AST_NODE_TYPES.TSIntersectionType
diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts b/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts
index b472f75e5a0a..9900c20ed22e 100644
--- a/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts
+++ b/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts
@@ -5,14 +5,14 @@ import * as ts from 'typescript';
import { createRule, getParserServices, isStrongPrecedenceNode } from '../util';
-type MessageIds =
+export type MessageIds =
| 'comparingNullableToFalse'
| 'comparingNullableToTrueDirect'
| 'comparingNullableToTrueNegated'
| 'direct'
| 'negated';
-type Options = [
+export type Options = [
{
allowComparingNullableBooleansToTrue?: boolean;
allowComparingNullableBooleansToFalse?: boolean;
diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-template-expression.ts b/packages/eslint-plugin/src/rules/no-unnecessary-template-expression.ts
index 44e10c5e33c8..aa35bda65f44 100644
--- a/packages/eslint-plugin/src/rules/no-unnecessary-template-expression.ts
+++ b/packages/eslint-plugin/src/rules/no-unnecessary-template-expression.ts
@@ -11,7 +11,7 @@ import {
isUndefinedIdentifier,
} from '../util';
-type MessageId = 'noUnnecessaryTemplateExpression';
+export type MessageId = 'noUnnecessaryTemplateExpression';
export default createRule<[], MessageId>({
name: 'no-unnecessary-template-expression',
diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts
index d482ef1b672a..711565a11d96 100644
--- a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts
+++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts
@@ -20,7 +20,7 @@ type ParameterCapableTSNode =
| ts.TypeQueryNode
| ts.TypeReferenceNode;
-type MessageIds = 'unnecessaryTypeParameter';
+export type MessageIds = 'unnecessaryTypeParameter';
export default createRule<[], MessageIds>({
name: 'no-unnecessary-type-arguments',
diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts
index 69f5daaad619..b128178b6bc1 100644
--- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts
+++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts
@@ -15,12 +15,12 @@ import {
NullThrowsReasons,
} from '../util';
-type Options = [
+export type Options = [
{
typesToIgnore?: string[];
},
];
-type MessageIds = 'contextuallyUnnecessary' | 'unnecessaryAssertion';
+export type MessageIds = 'contextuallyUnnecessary' | 'unnecessaryAssertion';
export default createRule({
name: 'no-unnecessary-type-assertion',
diff --git a/packages/eslint-plugin/src/rules/no-unsafe-argument.ts b/packages/eslint-plugin/src/rules/no-unsafe-argument.ts
index 08950fa8d732..3b03af48065b 100644
--- a/packages/eslint-plugin/src/rules/no-unsafe-argument.ts
+++ b/packages/eslint-plugin/src/rules/no-unsafe-argument.ts
@@ -12,7 +12,7 @@ import {
nullThrows,
} from '../util';
-type MessageIds =
+export type MessageIds =
| 'unsafeArgument'
| 'unsafeArraySpread'
| 'unsafeSpread'
diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts
index f496f02d9155..068dfe92a4b0 100644
--- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts
+++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts
@@ -9,7 +9,7 @@ import {
isTypeAnyType,
} from '../util';
-type MessageIds =
+export type MessageIds =
| 'unsafeCall'
| 'unsafeCallThis'
| 'unsafeNew'
diff --git a/packages/eslint-plugin/src/rules/no-unsafe-unary-minus.ts b/packages/eslint-plugin/src/rules/no-unsafe-unary-minus.ts
index 66488e37124a..a95633404d1d 100644
--- a/packages/eslint-plugin/src/rules/no-unsafe-unary-minus.ts
+++ b/packages/eslint-plugin/src/rules/no-unsafe-unary-minus.ts
@@ -3,8 +3,8 @@ import * as ts from 'typescript';
import * as util from '../util';
-type Options = [];
-type MessageIds = 'unaryMinus';
+export type Options = [];
+export type MessageIds = 'unaryMinus';
export default util.createRule({
name: 'no-unsafe-unary-minus',
diff --git a/packages/eslint-plugin/src/rules/no-unused-expressions.ts b/packages/eslint-plugin/src/rules/no-unused-expressions.ts
index 83c2ddd6c527..aab3bbf4f5b5 100644
--- a/packages/eslint-plugin/src/rules/no-unused-expressions.ts
+++ b/packages/eslint-plugin/src/rules/no-unused-expressions.ts
@@ -9,8 +9,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-unused-expressions');
-type MessageIds = InferMessageIdsTypeFromRule;
-type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
export default createRule({
name: 'no-unused-expressions',
diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts
index d577773de9ef..551bfe9ecc39 100644
--- a/packages/eslint-plugin/src/rules/no-use-before-define.ts
+++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts
@@ -219,7 +219,7 @@ function isInInitializer(
return false;
}
-interface Config {
+export interface Config {
functions?: boolean;
classes?: boolean;
enums?: boolean;
@@ -228,8 +228,8 @@ interface Config {
ignoreTypeReferences?: boolean;
allowNamedExports?: boolean;
}
-type Options = [Config | 'nofunc'];
-type MessageIds = 'noUseBeforeDefine';
+export type Options = [Config | 'nofunc'];
+export type MessageIds = 'noUseBeforeDefine';
export default createRule({
name: 'no-use-before-define',
diff --git a/packages/eslint-plugin/src/rules/no-useless-constructor.ts b/packages/eslint-plugin/src/rules/no-useless-constructor.ts
index dcfc7dd976de..460132f0cb33 100644
--- a/packages/eslint-plugin/src/rules/no-useless-constructor.ts
+++ b/packages/eslint-plugin/src/rules/no-useless-constructor.ts
@@ -10,8 +10,8 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('no-useless-constructor');
-type Options = InferOptionsTypeFromRule;
-type MessageIds = InferMessageIdsTypeFromRule;
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
/**
* Check if method with accessibility is not useless
diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts
index 7b13cd8e2e9a..3dd793e79831 100644
--- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts
+++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts
@@ -11,7 +11,7 @@ import {
isUndefinedIdentifier,
} from '../util';
-type MessageId = 'noUnnecessaryTemplateExpression';
+export type MessageId = 'noUnnecessaryTemplateExpression';
export default createRule<[], MessageId>({
name: 'no-useless-template-literals',
diff --git a/packages/eslint-plugin/src/rules/no-var-requires.ts b/packages/eslint-plugin/src/rules/no-var-requires.ts
index 9bb9fb7c1921..ec8e00078360 100644
--- a/packages/eslint-plugin/src/rules/no-var-requires.ts
+++ b/packages/eslint-plugin/src/rules/no-var-requires.ts
@@ -3,12 +3,12 @@ import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
import { createRule, getStaticStringValue } from '../util';
-type Options = [
+export type Options = [
{
allow: string[];
},
];
-type MessageIds = 'noVarReqs';
+export type MessageIds = 'noVarReqs';
export default createRule({
name: 'no-var-requires',
diff --git a/packages/eslint-plugin/src/rules/only-throw-error.ts b/packages/eslint-plugin/src/rules/only-throw-error.ts
index 62ce268fc700..bc30a31b1428 100644
--- a/packages/eslint-plugin/src/rules/only-throw-error.ts
+++ b/packages/eslint-plugin/src/rules/only-throw-error.ts
@@ -10,9 +10,9 @@ import {
isTypeUnknownType,
} from '../util';
-type MessageIds = 'object' | 'undef';
+export type MessageIds = 'object' | 'undef';
-type Options = [
+export type Options = [
{
allowThrowingAny?: boolean;
allowThrowingUnknown?: boolean;
diff --git a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts
index ecd87a06a643..be4227903475 100644
--- a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts
+++ b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts
@@ -35,14 +35,14 @@ interface NodeTestObject {
test: NodeTest;
}
-interface PaddingOption {
+export interface PaddingOption {
blankLine: keyof typeof PaddingTypes;
prev: string[] | string;
next: string[] | string;
}
-type MessageIds = 'expectedBlankLine' | 'unexpectedBlankLine';
-type Options = PaddingOption[];
+export type MessageIds = 'expectedBlankLine' | 'unexpectedBlankLine';
+export type Options = PaddingOption[];
const LT = `[${Array.from(
new Set(['\r\n', '\r', '\n', '\u2028', '\u2029']),
diff --git a/packages/eslint-plugin/src/rules/parameter-properties.ts b/packages/eslint-plugin/src/rules/parameter-properties.ts
index 5246594e912e..91408e162d37 100644
--- a/packages/eslint-plugin/src/rules/parameter-properties.ts
+++ b/packages/eslint-plugin/src/rules/parameter-properties.ts
@@ -3,7 +3,7 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule, nullThrows } from '../util';
-type Modifier =
+export type Modifier =
| 'private readonly'
| 'private'
| 'protected readonly'
@@ -12,16 +12,16 @@ type Modifier =
| 'public'
| 'readonly';
-type Prefer = 'class-property' | 'parameter-property';
+export type Prefer = 'class-property' | 'parameter-property';
-type Options = [
+export type Options = [
{
allow?: Modifier[];
prefer?: Prefer;
},
];
-type MessageIds = 'preferClassProperty' | 'preferParameterProperty';
+export type MessageIds = 'preferClassProperty' | 'preferParameterProperty';
export default createRule({
name: 'parameter-properties',
diff --git a/packages/eslint-plugin/src/rules/prefer-destructuring.ts b/packages/eslint-plugin/src/rules/prefer-destructuring.ts
index 60e53dbb61e6..a50f1f8fac46 100644
--- a/packages/eslint-plugin/src/rules/prefer-destructuring.ts
+++ b/packages/eslint-plugin/src/rules/prefer-destructuring.ts
@@ -13,13 +13,13 @@ import { getESLintCoreRule } from '../util/getESLintCoreRule';
const baseRule = getESLintCoreRule('prefer-destructuring');
-type BaseOptions = InferOptionsTypeFromRule;
-type EnforcementOptions = BaseOptions[1] & {
+export type BaseOptions = InferOptionsTypeFromRule;
+export type EnforcementOptions = BaseOptions[1] & {
enforceForDeclarationWithTypeAnnotation?: boolean;
};
-type Options = [BaseOptions[0], EnforcementOptions];
+export type Options = [BaseOptions[0], EnforcementOptions];
-type MessageIds = InferMessageIdsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
const destructuringTypeConfig: JSONSchema4 = {
type: 'object',
diff --git a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts
index 27572b4f8f7f..9ef9191cfa7d 100644
--- a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts
+++ b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts
@@ -2,7 +2,7 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
import { createRule } from '../util';
-type MessageIds = 'defineInitializer' | 'defineInitializerSuggestion';
+export type MessageIds = 'defineInitializer' | 'defineInitializerSuggestion';
export default createRule<[], MessageIds>({
name: 'prefer-enum-initializers',
diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/compareNodes.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/compareNodes.ts
index d9dce486ec91..7d6acc5671f8 100644
--- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/compareNodes.ts
+++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/compareNodes.ts
@@ -119,7 +119,7 @@ function compareByVisiting(
return NodeComparisonResult.Equal;
}
-type CompareNodesArgument = TSESTree.Node | null | undefined;
+export type CompareNodesArgument = TSESTree.Node | null | undefined;
function compareNodesUncached(
nodeA: TSESTree.Node,
nodeB: TSESTree.Node,
diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts
index 54c44f4edda9..dd7beac839a8 100644
--- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts
+++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts
@@ -56,7 +56,7 @@ export interface ValidOperand {
export interface InvalidOperand {
type: OperandValidity.Invalid;
}
-type Operand = ValidOperand | InvalidOperand;
+export type Operand = ValidOperand | InvalidOperand;
const NULLISH_FLAGS = ts.TypeFlags.Null | ts.TypeFlags.Undefined;
function isValidFalseBooleanCheckType(
diff --git a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts
index 6cb935c1db9a..9329d2c757fc 100644
--- a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts
+++ b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts
@@ -10,7 +10,7 @@ import {
readonlynessOptionsSchema,
} from '../util';
-type Options = [
+export type Options = [
{
allow?: TypeOrValueSpecifier[];
checkParameterProperties?: boolean;
@@ -18,7 +18,7 @@ type Options = [
treatMethodsAsReadonly?: boolean;
},
];
-type MessageIds = 'shouldBeReadonly';
+export type MessageIds = 'shouldBeReadonly';
export default createRule({
name: 'prefer-readonly-parameter-types',
diff --git a/packages/eslint-plugin/src/rules/prefer-readonly.ts b/packages/eslint-plugin/src/rules/prefer-readonly.ts
index f163b1aaab34..02adbc4640d0 100644
--- a/packages/eslint-plugin/src/rules/prefer-readonly.ts
+++ b/packages/eslint-plugin/src/rules/prefer-readonly.ts
@@ -14,8 +14,8 @@ import {
getParameterPropertyHeadLoc,
} from '../util/getMemberHeadLoc';
-type MessageIds = 'preferReadonly';
-type Options = [
+export type MessageIds = 'preferReadonly';
+export type Options = [
{
onlyInlineLambdas?: boolean;
},
diff --git a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts
index f6a22152043c..ac139d381e77 100644
--- a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts
+++ b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts
@@ -16,7 +16,7 @@ import {
const EQ_OPERATORS = /^[=!]=/;
const regexpp = new RegExpParser();
-type AllowedSingleElementEquality = 'always' | 'never';
+export type AllowedSingleElementEquality = 'always' | 'never';
export type Options = [
{
@@ -24,7 +24,7 @@ export type Options = [
},
];
-type MessageIds = 'preferEndsWith' | 'preferStartsWith';
+export type MessageIds = 'preferEndsWith' | 'preferStartsWith';
export default createRule({
name: 'prefer-string-starts-ends-with',
diff --git a/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts b/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts
index 6ae1f11720e1..4a6851686383 100644
--- a/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts
+++ b/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts
@@ -4,7 +4,7 @@ import type { RuleFix, RuleFixer } from '@typescript-eslint/utils/ts-eslint';
import { createRule } from '../util';
-type MessageIds = 'preferExpectErrorComment';
+export type MessageIds = 'preferExpectErrorComment';
export default createRule<[], MessageIds>({
name: 'prefer-ts-expect-error',
diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts
index c191ce45b0db..3cb6a8c9b582 100644
--- a/packages/eslint-plugin/src/rules/promise-function-async.ts
+++ b/packages/eslint-plugin/src/rules/promise-function-async.ts
@@ -12,7 +12,7 @@ import {
NullThrowsReasons,
} from '../util';
-type Options = [
+export type Options = [
{
allowAny?: boolean;
allowedPromiseNames?: string[];
@@ -22,7 +22,7 @@ type Options = [
checkMethodDeclarations?: boolean;
},
];
-type MessageIds = 'missingAsync';
+export type MessageIds = 'missingAsync';
export default createRule({
name: 'promise-function-async',
diff --git a/packages/eslint-plugin/src/rules/require-types-exports.ts b/packages/eslint-plugin/src/rules/require-types-exports.ts
new file mode 100644
index 000000000000..222bdeb1edc7
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/require-types-exports.ts
@@ -0,0 +1,504 @@
+import {
+ ImplicitLibVariable,
+ ScopeType,
+} from '@typescript-eslint/scope-manager';
+import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
+import { AST_NODE_TYPES } from '@typescript-eslint/utils';
+
+import { createRule, findVariable } from '../util';
+
+export type MessageIds = 'requireTypeExport';
+
+export default createRule<[], MessageIds>({
+ name: 'require-types-exports',
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'Require exporting types that are used in exported entities',
+ recommended: 'strict',
+ },
+ messages: {
+ requireTypeExport:
+ '"{{ name }}" is used in other exports from this file, so it should also be exported.',
+ },
+ schema: [],
+ },
+ defaultOptions: [],
+ create(context) {
+ const externalizedTypes = new Set();
+ const reportedTypes = new Set();
+
+ function collectImportedTypes(
+ node:
+ | TSESTree.ImportSpecifier
+ | TSESTree.ImportNamespaceSpecifier
+ | TSESTree.ImportDefaultSpecifier,
+ ): void {
+ externalizedTypes.add(node.local.name);
+ }
+
+ function collectExportedTypes(node: TSESTree.Program): void {
+ const isCollectableType = (
+ node: TSESTree.Node,
+ ): node is
+ | TSESTree.TSTypeAliasDeclaration
+ | TSESTree.TSInterfaceDeclaration
+ | TSESTree.TSEnumDeclaration
+ | TSESTree.TSModuleDeclaration => {
+ return [
+ AST_NODE_TYPES.TSTypeAliasDeclaration,
+ AST_NODE_TYPES.TSInterfaceDeclaration,
+ AST_NODE_TYPES.TSEnumDeclaration,
+ AST_NODE_TYPES.TSModuleDeclaration,
+ ].includes(node.type);
+ };
+
+ node.body.forEach(statement => {
+ if (
+ statement.type === AST_NODE_TYPES.ExportNamedDeclaration &&
+ statement.declaration &&
+ isCollectableType(statement.declaration) &&
+ statement.declaration.id.type === AST_NODE_TYPES.Identifier
+ ) {
+ externalizedTypes.add(statement.declaration.id.name);
+ }
+ });
+ }
+
+ function visitExportedFunctionDeclaration(
+ node: (
+ | TSESTree.ExportNamedDeclaration
+ | TSESTree.DefaultExportDeclarations
+ | TSESTree.ArrowFunctionExpression
+ ) & {
+ declaration: TSESTree.FunctionDeclaration | TSESTree.TSDeclareFunction;
+ },
+ ): void {
+ checkNodeTypes(node.declaration);
+ }
+
+ function visitExportedVariableDeclaration(
+ node: TSESTree.ExportNamedDeclaration & {
+ declaration: TSESTree.VariableDeclaration;
+ },
+ ): void {
+ for (const declaration of node.declaration.declarations) {
+ checkNodeTypes(declaration);
+ }
+ }
+
+ function visitExportedTypeDeclaration(
+ node: TSESTree.ExportNamedDeclaration & {
+ declaration:
+ | TSESTree.TSTypeAliasDeclaration
+ | TSESTree.TSInterfaceDeclaration;
+ },
+ ): void {
+ checkNodeTypes(node.declaration);
+ }
+
+ function visitExportDefaultDeclaration(
+ node: TSESTree.ExportDefaultDeclaration,
+ ): void {
+ checkNodeTypes(node.declaration);
+ }
+
+ function checkNodeTypes(node: TSESTree.Node): void {
+ const { typeReferences, typeQueries } = getVisibleTypesRecursively(
+ node,
+ context.sourceCode,
+ );
+
+ typeReferences.forEach(checkTypeReference);
+ typeQueries.forEach(checkTypeQuery);
+ }
+
+ function checkTypeReference(node: TSESTree.TSTypeReference): void {
+ const name = getTypeName(node.typeName);
+
+ const isExternalized = externalizedTypes.has(name);
+ const isReported = reportedTypes.has(name);
+
+ if (isExternalized || isReported) {
+ return;
+ }
+
+ context.report({
+ node,
+ messageId: 'requireTypeExport',
+ data: {
+ name,
+ },
+ });
+
+ reportedTypes.add(name);
+ }
+
+ function checkTypeQuery(node: TSESTree.TSTypeQuery): void {
+ const name = context.sourceCode.getText(node);
+ const isReported = reportedTypes.has(name);
+
+ if (isReported) {
+ return;
+ }
+ context.report({
+ node,
+ messageId: 'requireTypeExport',
+ data: {
+ name,
+ },
+ });
+
+ reportedTypes.add(name);
+ }
+
+ return {
+ 'ImportDeclaration ImportSpecifier': collectImportedTypes,
+ 'ImportDeclaration ImportNamespaceSpecifier': collectImportedTypes,
+ 'ImportDeclaration ImportDefaultSpecifier': collectImportedTypes,
+
+ Program: collectExportedTypes,
+
+ 'ExportNamedDeclaration[declaration.type="FunctionDeclaration"]':
+ visitExportedFunctionDeclaration,
+
+ 'ExportNamedDeclaration[declaration.type="TSDeclareFunction"]':
+ visitExportedFunctionDeclaration,
+
+ 'ExportDefaultDeclaration[declaration.type="FunctionDeclaration"]':
+ visitExportedFunctionDeclaration,
+
+ 'ExportDefaultDeclaration[declaration.type="ArrowFunctionExpression"]':
+ visitExportedFunctionDeclaration,
+
+ 'ExportNamedDeclaration[declaration.type="VariableDeclaration"]':
+ visitExportedVariableDeclaration,
+
+ 'ExportNamedDeclaration[declaration.type="TSTypeAliasDeclaration"]':
+ visitExportedTypeDeclaration,
+
+ 'ExportNamedDeclaration[declaration.type="TSTypeAliasDeclaration"] > ExportNamedDeclaration[declaration.type="TSInterfaceDeclaration"]':
+ visitExportedTypeDeclaration,
+
+ 'ExportNamedDeclaration[declaration.type="TSInterfaceDeclaration"]':
+ visitExportedTypeDeclaration,
+
+ 'ExportNamedDeclaration[declaration.type="TSModuleDeclaration"] > ExportNamedDeclaration[declaration.type="TSInterfaceDeclaration"]':
+ visitExportedTypeDeclaration,
+
+ ExportDefaultDeclaration: visitExportDefaultDeclaration,
+ };
+ },
+});
+
+function getTypeName(typeName: TSESTree.EntityName): string {
+ switch (typeName.type) {
+ case AST_NODE_TYPES.Identifier:
+ return typeName.name;
+
+ case AST_NODE_TYPES.TSQualifiedName:
+ // Namespaced types are not exported directly, so we check the
+ // leftmost part of the name.
+ return getTypeName(typeName.left);
+
+ case AST_NODE_TYPES.ThisExpression:
+ return 'this';
+ }
+}
+
+function getVisibleTypesRecursively(
+ node: TSESTree.Node,
+ sourceCode: TSESLint.SourceCode,
+): {
+ typeReferences: Set;
+ typeQueries: Set;
+} {
+ const typeReferences = new Set();
+ const typeQueries = new Set();
+ const visited = new Set();
+
+ collect(node);
+
+ function collect(node: TSESTree.Node | null | undefined): void {
+ if (!node || visited.has(node)) {
+ return;
+ }
+
+ visited.add(node);
+
+ switch (node.type) {
+ case AST_NODE_TYPES.VariableDeclarator:
+ collect(node.id);
+ collect(node.init);
+ break;
+
+ case AST_NODE_TYPES.Identifier: {
+ collect(node.typeAnnotation?.typeAnnotation);
+
+ // Resolve the variable to its declaration (in cases where the variable is referenced)
+ const scope = sourceCode.getScope(node);
+ const variableNode = findVariable(scope, node.name);
+
+ variableNode?.defs.forEach(def => {
+ collect(def.name);
+ collect(def.node);
+ });
+ break;
+ }
+
+ case AST_NODE_TYPES.ObjectExpression:
+ node.properties.forEach(property => {
+ const nodeToCheck =
+ property.type === AST_NODE_TYPES.Property
+ ? property.value
+ : property.argument;
+
+ collect(nodeToCheck);
+ });
+ break;
+
+ case AST_NODE_TYPES.ArrayExpression:
+ node.elements.forEach(element => {
+ const nodeToCheck =
+ element?.type === AST_NODE_TYPES.SpreadElement
+ ? element.argument
+ : element;
+
+ collect(nodeToCheck);
+ });
+ break;
+
+ case AST_NODE_TYPES.NewExpression:
+ case AST_NODE_TYPES.CallExpression:
+ collect(node.callee);
+ node.arguments.forEach(arg => collect(arg));
+ node.typeArguments?.params.forEach(param => collect(param));
+ break;
+
+ case AST_NODE_TYPES.BinaryExpression:
+ case AST_NODE_TYPES.LogicalExpression:
+ collect(node.left);
+ collect(node.right);
+ break;
+
+ case AST_NODE_TYPES.ConditionalExpression:
+ collect(node.consequent);
+ collect(node.alternate);
+ break;
+
+ case AST_NODE_TYPES.ArrowFunctionExpression:
+ case AST_NODE_TYPES.FunctionDeclaration:
+ case AST_NODE_TYPES.FunctionExpression:
+ case AST_NODE_TYPES.TSDeclareFunction:
+ node.typeParameters?.params.forEach(param => collect(param.constraint));
+ node.params.forEach(collect);
+ collect(node.returnType?.typeAnnotation);
+
+ if (node.body) {
+ collectFunctionReturnStatements(node).forEach(collect);
+ }
+ break;
+
+ case AST_NODE_TYPES.AssignmentPattern:
+ collect(node.left);
+ break;
+
+ case AST_NODE_TYPES.RestElement:
+ collect(node.argument);
+ collect(node.typeAnnotation?.typeAnnotation);
+ break;
+
+ case AST_NODE_TYPES.ObjectPattern:
+ node.properties.forEach(collect);
+ collect(node.typeAnnotation?.typeAnnotation);
+ break;
+
+ case AST_NODE_TYPES.ArrayPattern:
+ node.elements.forEach(collect);
+ collect(node.typeAnnotation?.typeAnnotation);
+
+ break;
+
+ case AST_NODE_TYPES.ReturnStatement:
+ collect(node.argument);
+ break;
+
+ case AST_NODE_TYPES.TSTypeReference: {
+ const scope = sourceCode.getScope(node);
+ const variable = findVariable(scope, getTypeName(node.typeName));
+
+ const isBuiltinType = variable instanceof ImplicitLibVariable;
+
+ const isGenericTypeArg =
+ (variable?.scope.type === ScopeType.function ||
+ variable?.scope.type === ScopeType.type) &&
+ variable.identifiers.every(
+ id => id.parent.type === AST_NODE_TYPES.TSTypeParameter,
+ );
+
+ if (!isBuiltinType && !isGenericTypeArg) {
+ typeReferences.add(node);
+ }
+
+ node.typeArguments?.params.forEach(collect);
+ break;
+ }
+
+ case AST_NODE_TYPES.TSTypeOperator:
+ collect(node.typeAnnotation);
+ break;
+
+ case AST_NODE_TYPES.TSTypeQuery:
+ if (isInsideFunctionDeclaration(node)) {
+ typeQueries.add(node);
+ }
+ break;
+
+ case AST_NODE_TYPES.TSArrayType:
+ collect(node.elementType);
+ break;
+
+ case AST_NODE_TYPES.TSTupleType:
+ node.elementTypes.forEach(collect);
+ break;
+
+ case AST_NODE_TYPES.TSUnionType:
+ case AST_NODE_TYPES.TSIntersectionType:
+ node.types.forEach(collect);
+ break;
+
+ case AST_NODE_TYPES.TSTypeLiteral:
+ node.members.forEach(collect);
+ break;
+
+ case AST_NODE_TYPES.TSTemplateLiteralType:
+ node.types.forEach(collect);
+ break;
+
+ case AST_NODE_TYPES.TSTypeAliasDeclaration:
+ collect(node.typeAnnotation);
+ break;
+
+ case AST_NODE_TYPES.TSInterfaceDeclaration:
+ node.body.body.forEach(collect);
+ break;
+
+ case AST_NODE_TYPES.TSPropertySignature:
+ collect(node.typeAnnotation?.typeAnnotation);
+ break;
+
+ case AST_NODE_TYPES.TSQualifiedName:
+ collect(node.parent);
+ break;
+
+ case AST_NODE_TYPES.TSAsExpression:
+ collect(node.expression);
+ collect(node.typeAnnotation);
+ break;
+
+ case AST_NODE_TYPES.TSIndexedAccessType:
+ collect(node.objectType);
+ collect(node.indexType);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return {
+ typeReferences,
+ typeQueries,
+ };
+}
+
+function isInsideFunctionDeclaration(node: TSESTree.Node): boolean {
+ const functionNodes = new Set([
+ AST_NODE_TYPES.ArrowFunctionExpression,
+ AST_NODE_TYPES.FunctionDeclaration,
+ AST_NODE_TYPES.FunctionExpression,
+ AST_NODE_TYPES.TSDeclareFunction,
+ ]);
+
+ if (!node.parent) {
+ return false;
+ }
+
+ if (functionNodes.has(node.parent.type)) {
+ return true;
+ }
+
+ return isInsideFunctionDeclaration(node.parent);
+}
+
+function collectFunctionReturnStatements(
+ functionNode:
+ | TSESTree.ArrowFunctionExpression
+ | TSESTree.FunctionDeclaration
+ | TSESTree.FunctionExpression,
+): Set {
+ const isArrowFunctionReturn =
+ functionNode.type === AST_NODE_TYPES.ArrowFunctionExpression &&
+ functionNode.body.type !== AST_NODE_TYPES.BlockStatement;
+
+ if (isArrowFunctionReturn) {
+ return new Set([functionNode.body]);
+ }
+
+ const returnStatements = new Set();
+
+ forEachReturnStatement(functionNode, returnNode =>
+ returnStatements.add(returnNode),
+ );
+
+ return returnStatements;
+}
+
+// Heavily inspired by:
+// https://github.com/typescript-eslint/typescript-eslint/blob/103de6eed/packages/eslint-plugin/src/util/astUtils.ts#L47-L80
+function forEachReturnStatement(
+ functionNode:
+ | TSESTree.ArrowFunctionExpression
+ | TSESTree.FunctionDeclaration
+ | TSESTree.FunctionExpression,
+ visitor: (returnNode: TSESTree.ReturnStatement) => void,
+): void {
+ return traverse(functionNode.body);
+
+ function traverse(node: TSESTree.Node | null): void {
+ switch (node?.type) {
+ case AST_NODE_TYPES.ReturnStatement:
+ return visitor(node);
+
+ case AST_NODE_TYPES.SwitchStatement:
+ return node.cases.forEach(traverse);
+
+ case AST_NODE_TYPES.SwitchCase:
+ return node.consequent.forEach(traverse);
+
+ case AST_NODE_TYPES.BlockStatement:
+ return node.body.forEach(traverse);
+
+ case AST_NODE_TYPES.DoWhileStatement:
+ case AST_NODE_TYPES.ForInStatement:
+ case AST_NODE_TYPES.ForOfStatement:
+ case AST_NODE_TYPES.WhileStatement:
+ case AST_NODE_TYPES.ForStatement:
+ case AST_NODE_TYPES.WithStatement:
+ case AST_NODE_TYPES.CatchClause:
+ case AST_NODE_TYPES.LabeledStatement:
+ return traverse(node.body);
+
+ case AST_NODE_TYPES.IfStatement:
+ traverse(node.consequent);
+ traverse(node.alternate);
+ return;
+
+ case AST_NODE_TYPES.TryStatement:
+ traverse(node.block);
+ traverse(node.handler);
+ traverse(node.finalizer);
+ return;
+ }
+ }
+}
diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts
index 1953c15c70cb..f70603498d8f 100644
--- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts
+++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts
@@ -11,7 +11,7 @@ import {
isTypeFlagSet,
} from '../util';
-type Options = [
+export type Options = [
{
allowAny?: boolean;
allowBoolean?: boolean;
@@ -22,7 +22,7 @@ type Options = [
},
];
-type MessageIds = 'bigintAndNumber' | 'invalid' | 'mismatched';
+export type MessageIds = 'bigintAndNumber' | 'invalid' | 'mismatched';
export default createRule({
name: 'restrict-plus-operands',
diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts
index cc719fe7fb7b..3447ddab7b3c 100644
--- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts
+++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts
@@ -49,11 +49,11 @@ const optionTesters = (
option: `allow${type}` as const,
tester,
}));
-type Options = [
+export type Options = [
{ [Type in (typeof optionTesters)[number]['option']]?: boolean },
];
-type MessageId = 'invalidType';
+export type MessageId = 'invalidType';
export default createRule({
name: 'restrict-template-expressions',
diff --git a/packages/eslint-plugin/src/rules/space-before-function-paren.ts b/packages/eslint-plugin/src/rules/space-before-function-paren.ts
index 78ea98239db7..3d8c813ccc46 100644
--- a/packages/eslint-plugin/src/rules/space-before-function-paren.ts
+++ b/packages/eslint-plugin/src/rules/space-before-function-paren.ts
@@ -4,8 +4,8 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { createRule, isOpeningParenToken } from '../util';
-type Option = 'always' | 'never';
-type FuncOption = Option | 'ignore';
+export type Option = 'always' | 'never';
+export type FuncOption = Option | 'ignore';
export type Options = [
| Option
diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts
index 1452d7de6cdb..e9a784c6374d 100644
--- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts
+++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts
@@ -20,7 +20,7 @@ interface SwitchMetadata {
readonly containsNonLiteralType: boolean;
}
-type Options = [
+export type Options = [
{
/**
* If `true`, allow `default` cases on switch statements with exhaustive
@@ -39,7 +39,7 @@ type Options = [
},
];
-type MessageIds =
+export type MessageIds =
| 'switchIsNotExhaustive'
| 'dangerousDefaultCase'
| 'addMissingCases';
diff --git a/packages/eslint-plugin/src/rules/triple-slash-reference.ts b/packages/eslint-plugin/src/rules/triple-slash-reference.ts
index a45662c33d4c..7bec0ae018bf 100644
--- a/packages/eslint-plugin/src/rules/triple-slash-reference.ts
+++ b/packages/eslint-plugin/src/rules/triple-slash-reference.ts
@@ -3,14 +3,14 @@ import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils';
import { createRule } from '../util';
-type Options = [
+export type Options = [
{
lib?: 'always' | 'never';
path?: 'always' | 'never';
types?: 'always' | 'never' | 'prefer-import';
},
];
-type MessageIds = 'tripleSlashReference';
+export type MessageIds = 'tripleSlashReference';
export default createRule({
name: 'triple-slash-reference',
diff --git a/packages/eslint-plugin/src/rules/type-annotation-spacing.ts b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts
index 47fbf80d8ac0..0f0988a3332b 100644
--- a/packages/eslint-plugin/src/rules/type-annotation-spacing.ts
+++ b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts
@@ -31,8 +31,8 @@ interface Config extends WhitespaceRule {
type WhitespaceRules = Required;
-type Options = [Config?];
-type MessageIds =
+export type Options = [Config?];
+export type MessageIds =
| 'expectedSpaceAfter'
| 'expectedSpaceBefore'
| 'unexpectedSpaceAfter'
diff --git a/packages/eslint-plugin/src/rules/typedef.ts b/packages/eslint-plugin/src/rules/typedef.ts
index 187f4d620365..690386f2c9ce 100644
--- a/packages/eslint-plugin/src/rules/typedef.ts
+++ b/packages/eslint-plugin/src/rules/typedef.ts
@@ -14,9 +14,9 @@ const enum OptionKeys {
VariableDeclarationIgnoreFunction = 'variableDeclarationIgnoreFunction',
}
-type Options = { [k in OptionKeys]?: boolean };
+export type Options = { [k in OptionKeys]?: boolean };
-type MessageIds = 'expectedTypedef' | 'expectedTypedefNamed';
+export type MessageIds = 'expectedTypedef' | 'expectedTypedefNamed';
export default createRule<[Options], MessageIds>({
name: 'typedef',
diff --git a/packages/eslint-plugin/src/rules/unbound-method.ts b/packages/eslint-plugin/src/rules/unbound-method.ts
index 9b416ea4a570..6a87b9ea6f78 100644
--- a/packages/eslint-plugin/src/rules/unbound-method.ts
+++ b/packages/eslint-plugin/src/rules/unbound-method.ts
@@ -14,7 +14,7 @@ import {
// Rule Definition
//------------------------------------------------------------------------------
-interface Config {
+export interface Config {
ignoreStatic: boolean;
}
diff --git a/packages/eslint-plugin/src/rules/unified-signatures.ts b/packages/eslint-plugin/src/rules/unified-signatures.ts
index c8fa2f85f7df..14db3f84df78 100644
--- a/packages/eslint-plugin/src/rules/unified-signatures.ts
+++ b/packages/eslint-plugin/src/rules/unified-signatures.ts
@@ -51,12 +51,12 @@ type MethodDefinition =
| TSESTree.MethodDefinition
| TSESTree.TSAbstractMethodDefinition;
-type MessageIds =
+export type MessageIds =
| 'omittingRestParameter'
| 'omittingSingleParameter'
| 'singleParameterDifference';
-type Options = [
+export type Options = [
{
ignoreDifferentlyNamedParameters?: boolean;
},
diff --git a/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts b/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts
index b899b23c391d..74d09f305701 100644
--- a/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts
+++ b/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts
@@ -16,7 +16,7 @@ import {
nullThrows,
} from '../util';
-type MessageIds =
+export type MessageIds =
| 'useUnknown'
| 'useUnknownSpreadArgs'
| 'useUnknownArrayDestructuringPattern'
diff --git a/packages/eslint-plugin/src/util/getESLintCoreRule.ts b/packages/eslint-plugin/src/util/getESLintCoreRule.ts
index be28069d2877..761d7e8d0f82 100644
--- a/packages/eslint-plugin/src/util/getESLintCoreRule.ts
+++ b/packages/eslint-plugin/src/util/getESLintCoreRule.ts
@@ -1,7 +1,7 @@
import { ESLintUtils } from '@typescript-eslint/utils';
import { builtinRules } from 'eslint/use-at-your-own-risk';
-interface RuleMap {
+export interface RuleMap {
/* eslint-disable @typescript-eslint/consistent-type-imports -- more concise to use inline imports */
'arrow-parens': typeof import('eslint/lib/rules/arrow-parens');
'block-spacing': typeof import('eslint/lib/rules/block-spacing');
@@ -42,7 +42,7 @@ interface RuleMap {
/* eslint-enable @typescript-eslint/consistent-type-imports */
}
-type RuleId = keyof RuleMap;
+export type RuleId = keyof RuleMap;
export const getESLintCoreRule = (ruleId: R): RuleMap[R] =>
ESLintUtils.nullThrows(
diff --git a/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts b/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts
index a8f6bc40afbd..f1aa6c9ce68c 100644
--- a/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts
+++ b/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts
@@ -5,7 +5,7 @@ import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
import { isArrowToken, isOpeningParenToken } from './astUtils';
-type FunctionNode =
+export type FunctionNode =
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression;
diff --git a/packages/eslint-plugin/src/util/getOperatorPrecedence.ts b/packages/eslint-plugin/src/util/getOperatorPrecedence.ts
index 8aac3d6fbd2d..d14c05e963a6 100644
--- a/packages/eslint-plugin/src/util/getOperatorPrecedence.ts
+++ b/packages/eslint-plugin/src/util/getOperatorPrecedence.ts
@@ -295,7 +295,7 @@ export function getOperatorPrecedenceForNode(
}
}
-type TSESTreeOperatorKind =
+export type TSESTreeOperatorKind =
| ValueOf
| ValueOf;
diff --git a/packages/eslint-plugin/src/util/getWrappingFixer.ts b/packages/eslint-plugin/src/util/getWrappingFixer.ts
index d5d07b6ba7e1..4c95b7fd4db4 100644
--- a/packages/eslint-plugin/src/util/getWrappingFixer.ts
+++ b/packages/eslint-plugin/src/util/getWrappingFixer.ts
@@ -5,7 +5,7 @@ import {
ESLintUtils,
} from '@typescript-eslint/utils';
-interface WrappingFixerParams {
+export interface WrappingFixerParams {
/** Source code. */
sourceCode: Readonly;
/** The node we want to modify. */
diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/require-types-exports.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/require-types-exports.shot
new file mode 100644
index 000000000000..f515f012d362
--- /dev/null
+++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/require-types-exports.shot
@@ -0,0 +1,59 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Validating rule docs require-types-exports.mdx code examples ESLint output 1`] = `
+"Incorrect
+
+type Arg = string;
+type Result = number;
+
+export function strLength(arg: Arg): Result {
+ ~~~ Expected type "Arg" to be exported
+ ~~~~~~ Expected type "Result" to be exported
+ return arg.length;
+}
+
+interface Fruit {
+ name: string;
+ color: string;
+}
+
+export const getFruitName = (fruit: Fruit) => fruit.name;
+ ~~~~~ Expected type "Fruit" to be exported
+
+enum Color {
+ Red = 'red',
+ Green = 'green',
+ Blue = 'blue',
+}
+
+export declare function getRandomColor(): Color;
+ ~~~~~ Expected type "Color" to be exported
+"
+`;
+
+exports[`Validating rule docs require-types-exports.mdx code examples ESLint output 2`] = `
+"Correct
+
+export type Arg = string;
+export type Result = number;
+
+export function strLength(arg: Arg): Result {
+ return arg.length;
+}
+
+export interface Fruit {
+ name: string;
+ color: string;
+}
+
+export const getFruitName = (fruit: Fruit) => fruit.name;
+
+export enum Color {
+ Red = 'red',
+ Green = 'green',
+ Blue = 'blue',
+}
+
+export declare function getRandomColor(): Color;
+"
+`;
diff --git a/packages/eslint-plugin/tests/fixtures/tsconfig-with-dom.json b/packages/eslint-plugin/tests/fixtures/tsconfig-with-dom.json
new file mode 100644
index 000000000000..6168cfcb8d54
--- /dev/null
+++ b/packages/eslint-plugin/tests/fixtures/tsconfig-with-dom.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "lib": ["esnext", "DOM"]
+ }
+}
diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/createTestCases.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/createTestCases.ts
index 7e1207a222d2..90229443cf7c 100644
--- a/packages/eslint-plugin/tests/rules/naming-convention/cases/createTestCases.ts
+++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/createTestCases.ts
@@ -77,7 +77,7 @@ const IGNORED_FILTER = {
regex: /.gnored/.source,
};
-type Cases = { code: string[]; options: Omit }[];
+export type Cases = { code: string[]; options: Omit }[];
export function createTestCases(cases: Cases): void {
const createValidTestCases = (): TSESLint.ValidTestCase[] =>
diff --git a/packages/eslint-plugin/tests/rules/prefer-optional-chain/base-cases.ts b/packages/eslint-plugin/tests/rules/prefer-optional-chain/base-cases.ts
index 28b75a91697d..d61a5556ccca 100644
--- a/packages/eslint-plugin/tests/rules/prefer-optional-chain/base-cases.ts
+++ b/packages/eslint-plugin/tests/rules/prefer-optional-chain/base-cases.ts
@@ -5,8 +5,8 @@ import type {
PreferOptionalChainOptions,
} from '../../../src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions';
-type MutateFn = (c: string) => string;
-type BaseCaseCreator = (args: {
+export type MutateFn = (c: string) => string;
+export type BaseCaseCreator = (args: {
operator: '&&' | '||';
mutateCode?: MutateFn;
mutateOutput?: MutateFn;
diff --git a/packages/eslint-plugin/tests/rules/require-types-exports.test.ts b/packages/eslint-plugin/tests/rules/require-types-exports.test.ts
new file mode 100644
index 000000000000..5629431c0711
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/require-types-exports.test.ts
@@ -0,0 +1,3493 @@
+import { RuleTester } from '@typescript-eslint/rule-tester';
+
+import rule from '../../src/rules/require-types-exports';
+import { getFixturesRootDir } from '../RuleTester';
+
+const rootPath = getFixturesRootDir();
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ tsconfigRootDir: rootPath,
+ project: './tsconfig-with-dom.json',
+ },
+});
+
+ruleTester.run('require-types-exports', rule, {
+ valid: [
+ 'export function f(): void {}',
+ 'export const f = (): void => {};',
+
+ 'export function f(a: number): void {}',
+ 'export const f = (a: number): void => {};',
+
+ 'export function f(a: any): void {}',
+ 'export const f = (a: any): void => {};',
+
+ 'export function f(a: null): void {}',
+ 'export const f = (a: null): void => {};',
+
+ 'export function f(a: string | number): void {}',
+ 'export const f = (a: string | number): void => {};',
+
+ 'export function f(a?: string | number): void {}',
+ 'export const f = (a?: string | number): void => {};',
+
+ 'export function f(a: number): string {}',
+ 'export const f = (a: number): string => {};',
+
+ 'export function f(...args: any[]): void {}',
+ 'export const f = (...args: any[]): void => {};',
+
+ 'export function f(...args: unknown[]): void {}',
+ 'export const f = (...args: unknown[]): void => {};',
+
+ 'export default function f(): void {}',
+ 'export default (): void => {};',
+
+ `
+ type A = number;
+ function f(a: A): A {
+ return a;
+ }
+ `,
+
+ `
+ type A = number;
+ const f = (a: A): A => a;
+ `,
+
+ `
+ type A = number;
+ type B = string;
+ function f(a: A | B): any {
+ return a;
+ }
+ `,
+
+ `
+ type A = number;
+ type B = string;
+ const f = (a: A | B): any => a;
+ `,
+
+ `
+ type A = number;
+ declare function f(a: A): void;
+ `,
+
+ `
+ type A = number;
+ function f({ a }: { a: A }): A {}
+ `,
+
+ `
+ type A = number;
+ const f = ({ a }: { a: A }): A => {};
+ `,
+
+ `
+ type A = number;
+ type B = string;
+ function f([a, b]: [A, B]): void {}
+ `,
+
+ `
+ type A = number;
+ type B = string;
+ const f = ([a, b]: [A, B]): void => {};
+ `,
+
+ `
+ type A = number;
+ function f(a: A): void {}
+ `,
+
+ `
+ type A = number;
+ const f = (a: A): void => {};
+ `,
+
+ `
+ interface A {
+ a: number;
+ }
+
+ function f(a: A): A {
+ return a;
+ }
+ `,
+
+ `
+ interface A {
+ a: number;
+ }
+
+ const f = (a: A): A => a;
+ `,
+
+ `
+ export type A = number;
+ export function f(a: A): void {}
+ `,
+
+ `
+ export type A = number;
+ export const f = (a: A): void => {};
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export function f(a: A | B): void {}
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export const f = (a: A | B): void => {};
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export function f(a: A & B): void {}
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export const f = (a: A & B): void => {};
+ `,
+
+ `
+ export type A = number;
+ export function f(...args: A[]): void {}
+ `,
+
+ `
+ export type A = number;
+ export const f = (...args: A[]): void => {};
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export function f(args: { a: A; b: B; c: number }): void {}
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export const f = (args: { a: A; b: B; c: number }): void => {};
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export function f(args: [A, B]): void {}
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export const f = (args: [A, B]): void => {};
+ `,
+
+ `
+ export type A = number;
+ export function f(a: A = 1): void {}
+ `,
+
+ `
+ export type A = number;
+ export const f = (a: A = 1): void => {};
+ `,
+
+ `
+ export type A = number;
+ export function f(): A {}
+ `,
+
+ `
+ export type A = number;
+ export const f = (): A => {};
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export function f(): A | B {}
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export const f = (): A | B => {};
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export function f(): A & B {}
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export const f = (): A & B => {};
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export function f(): [A, B] {}
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export const f = (): [A, B] => {};
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export function f(): { a: A; b: B } {}
+ `,
+
+ `
+ export type A = number;
+ export type B = string;
+ export const f = (): { a: A; b: B } => {};
+ `,
+
+ `
+ import { testFunction, type Arg } from './module';
+
+ export function f(a: Arg): void {}
+ `,
+
+ `
+ import { Arg } from './types';
+
+ export function f(a: Arg): void {}
+ `,
+
+ `
+ import type { Arg } from './types';
+
+ export function f(a: Arg): void {}
+ `,
+
+ `
+ import type { ImportedArg as Arg } from './types';
+
+ export function f(a: Arg): void {}
+ `,
+
+ `
+ import type { Arg } from './types';
+
+ export function f(a: T): void {}
+ `,
+
+ `
+ export type R = number;
+
+ export function f() {
+ const value: { num: R } = {
+ num: 1,
+ };
+
+ return value;
+ }
+ `,
+
+ `
+ import type { A } from './types';
+
+ export type T1 = number;
+
+ export interface T2 {
+ key: number;
+ }
+
+ export const value: { a: { b: { c: T1 } } } | [string, T2 | A] = {
+ a: {
+ b: {
+ c: 1,
+ },
+ },
+ };
+ `,
+
+ `
+ import type { A } from './types';
+
+ export type T1 = number;
+
+ export interface T2 {
+ key: number;
+ }
+
+ const value: { a: { b: { c: T1 } } } | [string, T2 | A] = {
+ a: {
+ b: {
+ c: 1,
+ },
+ },
+ };
+
+ export default value;
+ `,
+
+ `
+ export enum Fruit {
+ Apple,
+ Banana,
+ Cherry,
+ }
+
+ export function f(a: Fruit): void {}
+ `,
+
+ `
+ export function f(arg: Record>) {
+ return arg;
+ }
+ `,
+
+ `
+ export function f>>(arg: T) {
+ return arg;
+ }
+ `,
+
+ `
+ export function f string>>(arg: T) {
+ return arg;
+ }
+ `,
+
+ `
+ export class Wrapper {
+ work(other: this) {}
+ }
+ `,
+
+ `
+ export namespace A {
+ export namespace B {
+ export type C = number;
+ }
+ }
+
+ export function a(arg: A.B.C) {
+ return arg;
+ }
+ `,
+
+ `
+ import * as ts from 'typescript';
+
+ export function a(arg: ts.Type) {
+ return arg;
+ }
+ `,
+
+ `
+ import ts from 'typescript';
+
+ export function a(arg: ts.Type) {
+ return arg;
+ }
+ `,
+
+ `
+ declare const element: HTMLElement;
+
+ export default element;
+ `,
+
+ `
+ export const date: Date = new Date();
+ `,
+
+ `
+ import ts from 'typescript';
+
+ export enum Fruit {
+ Apple,
+ Banana,
+ Cherry,
+ }
+
+ declare const apple: Fruit.Apple;
+
+ export type A = number;
+ export type B = string;
+ export type C = boolean;
+
+ export interface D {
+ key: string;
+ }
+
+ function func>(
+ arg: T,
+ ): T | ts.Type {
+ return arg;
+ }
+
+ export const value = {
+ apple,
+ func,
+ };
+ `,
+
+ `
+ export function func1() {
+ return func2(1);
+ }
+
+ export type A = number;
+
+ export function func2(arg: A) {
+ return 1;
+ }
+ `,
+
+ 'export type ValueOf = T[keyof T];',
+
+ `
+ const fruits = { apple: 'apple' };
+ export type Fruits = typeof fruits;
+
+ export function getFruit(key: Key): Fruits[Key] {
+ return fruits[key];
+ }
+ `,
+
+ `
+ const fruits = { apple: 'apple' };
+
+ export function doWork(): number {
+ const fruit: keyof typeof fruits = 'apple';
+
+ return 1;
+ }
+ `,
+ ],
+
+ invalid: [
+ {
+ code: `
+ type Arg = number;
+
+ export function f(a: Arg): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 30,
+ endColumn: 33,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export const f = (a: Arg): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 30,
+ endColumn: 33,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export default function (a: Arg): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 37,
+ endColumn: 40,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export default (a: Arg): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 28,
+ endColumn: 31,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export function f(a: Arg, b: Arg): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 30,
+ endColumn: 33,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export const f = (a: Arg, b: Arg): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 30,
+ endColumn: 33,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export function f(a: Arg1, b: Arg2): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 39,
+ endColumn: 43,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export const f = (a: Arg1, b: Arg2): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 39,
+ endColumn: 43,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+
+ interface Arg2 {
+ a: string;
+ }
+
+ export function f(a: Arg1, b: Arg2): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 39,
+ endColumn: 43,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+
+ interface Arg2 {
+ a: string;
+ }
+
+ export const f = (a: Arg1, b: Arg2): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 39,
+ endColumn: 43,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export function f(a: Arg1 | Arg2): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export const f = (a: Arg1 | Arg2): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export function f(a: Arg1 & Arg2): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export const f = (a: Arg1 & Arg2): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export function f([a, b]: [Arg1, Arg2, number]): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 36,
+ endColumn: 40,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 42,
+ endColumn: 46,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+ type Arg3 = boolean;
+
+ export function f([a, b]: [Arg1, Arg2, number], c: Arg3): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 36,
+ endColumn: 40,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 42,
+ endColumn: 46,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 60,
+ endColumn: 64,
+ data: {
+ name: 'Arg3',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export const f = ([a, b]: [Arg1, Arg2, number]): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 36,
+ endColumn: 40,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 42,
+ endColumn: 46,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export function f({ a, b }: { a: Arg1; b: Arg2; c: number }): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 42,
+ endColumn: 46,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 51,
+ endColumn: 55,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export const f = ({ a, b }: { a: Arg1; b: Arg2; c: number }): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 42,
+ endColumn: 46,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 51,
+ endColumn: 55,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export function f(...args: Arg[]): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 36,
+ endColumn: 39,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export const f = (...args: Arg[]): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 36,
+ endColumn: 39,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export function f(a: Arg = 1): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 30,
+ endColumn: 33,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export const f = (a: Arg = 1): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 30,
+ endColumn: 33,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ enum Fruit {
+ Apple,
+ Banana,
+ Cherry,
+ }
+
+ export function f(a: Fruit): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 30,
+ endColumn: 35,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ enum Fruit {
+ Apple,
+ Banana,
+ Cherry,
+ }
+
+ export const f = (a: Fruit): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 30,
+ endColumn: 35,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export function f(a: T): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 37,
+ endColumn: 40,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export function f(a: T): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export function f(a: T): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export function f(a: T): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export const f = (a: T): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = string;
+
+ export function f(a: T): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 38,
+ endColumn: 41,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = string;
+
+ export function f(a: T): void {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 42,
+ endColumn: 45,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret = string;
+
+ export function f(): Ret {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 30,
+ endColumn: 33,
+ data: {
+ name: 'Ret',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret = string;
+
+ export const f = (): Ret => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 30,
+ endColumn: 33,
+ data: {
+ name: 'Ret',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export function f(): Ret1 | Ret2 {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export const f = (): Ret1 | Ret2 => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export function f(): Ret1 & Ret2 {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export const f = (): Ret1 & Ret2 => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 30,
+ endColumn: 34,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export function f(): [Ret1, Ret2, number, Ret1] {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 31,
+ endColumn: 35,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export const f = (): [Ret1, Ret2, number, Ret1] => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 31,
+ endColumn: 35,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export function f(): { a: Ret1; b: Ret2; c: number; d: Ret1 } {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 35,
+ endColumn: 39,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export const f = (): { a: Ret1; b: Ret2; c: number; d: Ret1 } => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 35,
+ endColumn: 39,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret = string;
+
+ export function f(): T {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 37,
+ endColumn: 40,
+ data: {
+ name: 'Ret',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export function f(): T {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export function f(): T {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export function f(): T {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret1 = string;
+ type Ret2 = number;
+
+ export function f(): T {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 41,
+ data: {
+ name: 'Ret1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 44,
+ endColumn: 48,
+ data: {
+ name: 'Ret2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Ret = string;
+
+ export function f(): T {}
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 38,
+ endColumn: 41,
+ data: {
+ name: 'Ret',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ const a = (a: Arg): void => {};
+
+ export default a;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 23,
+ endColumn: 26,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ const a = function (a: Arg): void {};
+
+ export default a;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 32,
+ endColumn: 35,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export declare function f(a: Arg): void;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 38,
+ endColumn: 41,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg = number;
+
+ export declare function f(a: Arg): Arg;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 38,
+ endColumn: 41,
+ data: {
+ name: 'Arg',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type R = number;
+
+ export function f() {
+ const value: { num: R } = {
+ num: 1,
+ };
+
+ return value;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 31,
+ endColumn: 32,
+ data: {
+ name: 'R',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = boolean;
+ type Ret = string;
+
+ export declare function f(
+ a: { b: { c: Arg1 | number | { d: T } } },
+ e: Arg1,
+ ): { a: { b: T | Ret } };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 45,
+ endColumn: 49,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 24,
+ endColumn: 28,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 9,
+ column: 26,
+ endColumn: 29,
+ data: {
+ name: 'Ret',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export declare function f(a: Arg1): true;
+ export declare function f(a: Arg2): false;
+ export declare function f(a: Arg1 | Arg2): boolean;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 38,
+ endColumn: 42,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 38,
+ endColumn: 42,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Arg1 = number;
+ type Arg2 = string;
+
+ export const f1 = (a: Arg1): void => {},
+ f2 = (a: Arg2): void => {};
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 31,
+ endColumn: 35,
+ data: {
+ name: 'Arg1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 20,
+ endColumn: 24,
+ data: {
+ name: 'Arg2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ namespace A {
+ export namespace B {
+ export type C = number;
+ }
+ }
+
+ export function a(arg: A.B.C) {
+ return arg;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 32,
+ endColumn: 37,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ namespace A {
+ export type B = number;
+ }
+
+ type B = string;
+
+ export function a(arg: B) {
+ return arg;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 32,
+ endColumn: 33,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ namespace A {
+ export interface B {
+ value: number;
+ }
+ }
+
+ type B = string;
+
+ export function a(arg: B) {
+ return arg;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 10,
+ column: 32,
+ endColumn: 33,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ namespace A {
+ export enum B {
+ Value1,
+ Value2,
+ }
+ }
+
+ type B = string;
+
+ export function a(arg: B) {
+ return arg;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 11,
+ column: 32,
+ endColumn: 33,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ namespace A {
+ export namespace B {
+ export type C = number;
+ }
+ }
+
+ type B = string;
+
+ export function a(arg: B) {
+ return arg;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 10,
+ column: 32,
+ endColumn: 33,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ import type { A } from './types';
+
+ type T1 = number;
+
+ interface T2 {
+ key: number;
+ }
+
+ export const value: { a: { b: { c: T1 } } } | [string, T2 | A] = {
+ a: {
+ b: {
+ c: 1,
+ },
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 10,
+ column: 44,
+ endColumn: 46,
+ data: {
+ name: 'T1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 10,
+ column: 64,
+ endColumn: 66,
+ data: {
+ name: 'T2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ import type { A } from './types';
+
+ type T1 = number;
+
+ interface T2 {
+ key: number;
+ }
+
+ const value: { a: { b: { c: T1 } } } | [string, T2 | A] = {
+ a: {
+ b: {
+ c: 1,
+ },
+ },
+ };
+
+ export default value;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 10,
+ column: 37,
+ endColumn: 39,
+ data: {
+ name: 'T1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 10,
+ column: 57,
+ endColumn: 59,
+ data: {
+ name: 'T2',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type T1 = number;
+
+ interface T2 {
+ key: number;
+ }
+
+ type T3 = boolean;
+
+ export const value:
+ | {
+ a: T1;
+ b: {
+ c: T2;
+ };
+ }
+ | T3[] = {
+ a: 1,
+ b: {
+ c: {
+ key: 1,
+ },
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 12,
+ column: 18,
+ endColumn: 20,
+ data: {
+ name: 'T1',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 14,
+ column: 20,
+ endColumn: 22,
+ data: {
+ name: 'T2',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 17,
+ column: 13,
+ endColumn: 15,
+ data: {
+ name: 'T3',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = string;
+ type B = string;
+
+ const apple: A = 'apple';
+ const banana: B = 'banana';
+
+ export const value = {
+ path: {
+ to: {
+ apple,
+ and: {
+ banana,
+ },
+ },
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 22,
+ endColumn: 23,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 23,
+ endColumn: 24,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = string;
+ type B = string;
+
+ const apple: A = 'apple';
+ const banana: B = 'banana';
+
+ const value = {
+ path: {
+ to: {
+ apple,
+ and: {
+ banana,
+ },
+ },
+ },
+ };
+
+ export default value;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 22,
+ endColumn: 23,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 23,
+ endColumn: 24,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = string;
+ type B = string;
+
+ const apple: A = 'apple';
+ const banana: B = 'banana';
+
+ const value = {
+ spreadObject: { ...{ apple } },
+ spreadArray: [...[banana]],
+ };
+
+ export default value;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 22,
+ endColumn: 23,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 23,
+ endColumn: 24,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Fruit = 'apple' | 'banana';
+
+ const apple: Fruit = 'apple';
+ const banana: Fruit = 'banana';
+
+ export const value = {
+ path: {
+ to: [apple, banana],
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 22,
+ endColumn: 27,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Fruit = 'apple' | 'banana';
+
+ const apple: Fruit = 'apple';
+ const banana: Fruit = 'banana';
+
+ export const value = {
+ path: {
+ to: [apple, banana] as const,
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 22,
+ endColumn: 27,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Fruit = 'apple' | 'banana';
+
+ const apple: Fruit = 'apple';
+ const banana: Fruit = 'banana';
+
+ export const value = {
+ path: {
+ to: [apple, banana] as any,
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 22,
+ endColumn: 27,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Fruit = 'apple' | 'banana';
+
+ const apple = 'apple';
+ const banana = 'banana';
+
+ export const value = {
+ path: {
+ to: [apple, banana] as [Fruit, Fruit],
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 9,
+ column: 37,
+ endColumn: 42,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Fruit = 'apple' | 'banana';
+
+ const apple = 'apple';
+ const banana = 'banana';
+
+ export const value = {
+ path: {
+ to: [apple, banana] as Fruit | number,
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 9,
+ column: 36,
+ endColumn: 41,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = string;
+ type C = boolean;
+ type D = symbol;
+
+ declare const a: [A, B] | ([Array, Set] & Exclude);
+
+ export const value = { a };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 27,
+ endColumn: 28,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 30,
+ endColumn: 31,
+ data: {
+ name: 'B',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 43,
+ endColumn: 44,
+ data: {
+ name: 'C',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 51,
+ endColumn: 52,
+ data: {
+ name: 'D',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = string;
+
+ export const value = {
+ func: (arg: A): B => 'apple',
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 23,
+ endColumn: 24,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 27,
+ endColumn: 28,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = string;
+
+ export const value = {
+ func: function (arg: A): B {
+ return 'apple';
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 32,
+ endColumn: 33,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 36,
+ endColumn: 37,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = string;
+
+ const func = (arg: A): B => 'apple';
+
+ export const value = {
+ func,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 28,
+ endColumn: 29,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 32,
+ endColumn: 33,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = string;
+
+ const func = function (arg: A): B {
+ return 'apple';
+ };
+
+ export const value = {
+ func,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 38,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ const func = (arg: T): T => 'apple';
+
+ export const value = {
+ func,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 33,
+ endColumn: 34,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ const func = function (arg: T): T {
+ return 'apple';
+ };
+
+ export const value = {
+ func,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 42,
+ endColumn: 43,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ export const value = {
+ func: (arg: T): T => 'apple',
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 28,
+ endColumn: 29,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ export const value = {
+ func: function (arg: T): T {
+ return 'apple';
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 37,
+ endColumn: 38,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ declare function func(arg: T): T;
+
+ export const value = {
+ func,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ enum Fruit {
+ Apple,
+ Banana,
+ Cherry,
+ }
+
+ declare function func(arg: T): T;
+
+ export const value = {
+ func,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 41,
+ endColumn: 46,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ enum Fruit {
+ Apple,
+ Banana,
+ Cherry,
+ }
+
+ declare const a: Fruit.Apple;
+
+ export const value = {
+ a,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 26,
+ endColumn: 37,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ enum Fruit {
+ Apple,
+ Banana,
+ Cherry,
+ }
+
+ declare const a: Fruit.Apple;
+
+ export const value = {
+ key: () => a,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 26,
+ endColumn: 37,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ enum Fruit {
+ Apple,
+ Banana,
+ Cherry,
+ }
+
+ declare const a: Fruit.Apple;
+
+ export const value = {
+ key: function () {
+ return a;
+ },
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 8,
+ column: 26,
+ endColumn: 37,
+ data: {
+ name: 'Fruit',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Item = {
+ key: string;
+ value: number;
+ };
+
+ type ItemKey = Item['key'];
+
+ const item: Item = { key: 'apple', value: 1 };
+
+ const map = new Map([['apple', item]]);
+
+ export const value = {
+ map,
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 9,
+ column: 21,
+ endColumn: 25,
+ data: {
+ name: 'Item',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 11,
+ column: 29,
+ endColumn: 36,
+ data: {
+ name: 'ItemKey',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ const item: A = 1;
+
+ export const value = {
+ key: (() => item)(),
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 21,
+ endColumn: 22,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ const item: A = 1;
+
+ export const value = {
+ key: ((a: A) => a)(item),
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 21,
+ endColumn: 22,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ const item: A = 1;
+
+ export const value = {
+ key: ((a: T) => a)(item),
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 28,
+ endColumn: 29,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ const item: A = 1;
+
+ export const value = {
+ key: ((a: A) => [a])(item),
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 21,
+ endColumn: 22,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ const item: A = 1;
+
+ export const value = {
+ key: ((a: A) => ({ a }))(item),
+ };
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 7,
+ column: 21,
+ endColumn: 22,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ export function func1(arg: R): R {
+ return func2(arg);
+ }
+
+ declare function func2(arg: T): T;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = string;
+
+ export function func1(arg: R): R {
+ doWork(String(arg));
+
+ return arg;
+ }
+
+ declare function doWork(arg: B): void;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = number;
+
+ export function func1(arg: R) {
+ return func2(arg);
+ }
+
+ declare function func2(arg: B): B;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 9,
+ column: 37,
+ endColumn: 38,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = number;
+ type C = number;
+
+ export function func1(arg: R) {
+ if (Math.random() > 0.5) {
+ return func2(arg);
+ } else {
+ return func3(arg);
+ }
+ }
+
+ declare function func2(arg: B): B;
+ declare function func3(arg: C): C;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 14,
+ column: 37,
+ endColumn: 38,
+ data: {
+ name: 'B',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 15,
+ column: 37,
+ endColumn: 38,
+ data: {
+ name: 'C',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = number;
+
+ export function func1(arg: R) {
+ const a = (() => {
+ return func2(arg);
+ })();
+
+ return arg;
+ }
+
+ declare function func2(arg: B): B;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = number;
+
+ export function func1(arg: R) {
+ return arg as B;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 25,
+ endColumn: 26,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = string;
+
+ export function func1(arg: R): R {
+ function doWork(arg2: B): void {}
+
+ doWork(String(arg));
+
+ return arg;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 41,
+ endColumn: 42,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type ItemsMap = Record;
+ type Key = keyof ItemsMap;
+
+ export function get(key: K): ItemsMap[K] {
+ return key as never;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 39,
+ endColumn: 42,
+ data: {
+ name: 'Key',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 53,
+ endColumn: 61,
+ data: {
+ name: 'ItemsMap',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ const value: A = 1;
+
+ export function func() {
+ return Math.random() > 0.5 && value;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 22,
+ endColumn: 23,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+ type B = string;
+
+ const valueA: A = 1;
+ const valueB: B = 'test';
+
+ export function func() {
+ return Math.random() > 0.5 ? valueA : valueB;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 23,
+ endColumn: 24,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 23,
+ endColumn: 24,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ declare function func(): string;
+
+ type A = string;
+
+ export default func();
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 6,
+ column: 29,
+ endColumn: 30,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type Apple = 'apple';
+ type Banana = 'banana';
+
+ export type Fruites = Apple | Banana;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 31,
+ endColumn: 36,
+ data: {
+ name: 'Apple',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 39,
+ endColumn: 45,
+ data: {
+ name: 'Banana',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ export interface B {
+ a: A;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 14,
+ endColumn: 15,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = number;
+
+ interface B {
+ b: string;
+ }
+
+ export namespace C {
+ export type D = A;
+ export type E = B;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 9,
+ column: 27,
+ endColumn: 28,
+ data: {
+ name: 'A',
+ },
+ },
+ {
+ messageId: 'requireTypeExport',
+ line: 10,
+ column: 27,
+ endColumn: 28,
+ data: {
+ name: 'B',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ type A = 'test';
+ export type B = \`test-\${A}\`;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 3,
+ column: 33,
+ endColumn: 34,
+ data: {
+ name: 'A',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ const fruits = { apple: 'apple' };
+
+ export function getFruit(
+ key: Key,
+ ): (typeof fruits)[Key] {
+ return fruits[key];
+ }
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 52,
+ endColumn: 65,
+ data: {
+ name: 'typeof fruits',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ const fruits = { apple: 'apple' };
+
+ export declare function processFruit(
+ fruit: F,
+ ): void;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 4,
+ column: 56,
+ endColumn: 75,
+ data: {
+ name: 'typeof fruits.apple',
+ },
+ },
+ ],
+ },
+
+ {
+ code: `
+ const fruits = { apple: 'apple' };
+
+ export declare function processFruit<
+ F extends Record,
+ >(fruit: F): void;
+ `,
+ errors: [
+ {
+ messageId: 'requireTypeExport',
+ line: 5,
+ column: 34,
+ endColumn: 47,
+ data: {
+ name: 'typeof fruits',
+ },
+ },
+ ],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/tests/schema-snapshots/require-types-exports.shot b/packages/eslint-plugin/tests/schema-snapshots/require-types-exports.shot
new file mode 100644
index 000000000000..2f0fbf6d8dfc
--- /dev/null
+++ b/packages/eslint-plugin/tests/schema-snapshots/require-types-exports.shot
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Rule schemas should be convertible to TS types for documentation purposes require-types-exports 1`] = `
+"
+# SCHEMA:
+
+[]
+
+
+# TYPES:
+
+/** No options declared */
+type Options = [];"
+`;
diff --git a/packages/typescript-eslint/src/configs/all.ts b/packages/typescript-eslint/src/configs/all.ts
index f0d48be1d34d..9a3334d387fe 100644
--- a/packages/typescript-eslint/src/configs/all.ts
+++ b/packages/typescript-eslint/src/configs/all.ts
@@ -156,6 +156,7 @@ export default (
'@typescript-eslint/require-array-sort-compare': 'error',
'require-await': 'off',
'@typescript-eslint/require-await': 'error',
+ '@typescript-eslint/require-types-exports': 'error',
'@typescript-eslint/restrict-plus-operands': 'error',
'@typescript-eslint/restrict-template-expressions': 'error',
'no-return-await': 'off',
diff --git a/packages/typescript-eslint/src/configs/strict-type-checked.ts b/packages/typescript-eslint/src/configs/strict-type-checked.ts
index 7aeb158346c3..85b43fa789eb 100644
--- a/packages/typescript-eslint/src/configs/strict-type-checked.ts
+++ b/packages/typescript-eslint/src/configs/strict-type-checked.ts
@@ -82,6 +82,7 @@ export default (
'@typescript-eslint/prefer-return-this-type': 'error',
'require-await': 'off',
'@typescript-eslint/require-await': 'error',
+ '@typescript-eslint/require-types-exports': 'error',
'@typescript-eslint/restrict-plus-operands': [
'error',
{
diff --git a/packages/typescript-eslint/src/configs/strict.ts b/packages/typescript-eslint/src/configs/strict.ts
index 71615896c4dd..f208b0a4a286 100644
--- a/packages/typescript-eslint/src/configs/strict.ts
+++ b/packages/typescript-eslint/src/configs/strict.ts
@@ -49,6 +49,7 @@ export default (
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/prefer-literal-enum-member': 'error',
+ '@typescript-eslint/require-types-exports': 'error',
'@typescript-eslint/triple-slash-reference': 'error',
'@typescript-eslint/unified-signatures': 'error',
},