Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): [no-type-alias]: add allowGenerics option #3865

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/eslint-plugin/docs/rules/no-type-alias.md
Expand Up @@ -89,6 +89,7 @@ or more of the following you may pass an object with the options set as follows:
- `allowLiterals` set to `"always"` will allow you to use type aliases with literal objects (Defaults to `"never"`)
- `allowMappedTypes` set to `"always"` will allow you to use type aliases as mapping tools (Defaults to `"never"`)
- `allowTupleTypes` set to `"always"` will allow you to use type aliases with tuples (Defaults to `"never"`)
- `allowGenerics` set to `"always"` will allow you to use type aliases with generics (Defaults to `"never"`)

### `allowAliases`

Expand Down Expand Up @@ -555,6 +556,28 @@ type Foo = [number] & [number, number];
type Foo = [string] | [number];
```

### `allowGenerics`

This applies to generic types, including TypeScript provided global utility types (`type Foo = Record<string, number>`).

The setting accepts the following options:

- `"always"` or `"never"` to active or deactivate the feature.

Examples of **correct** code for the `{ "allowGenerics": "always" }` options:

```ts
type Foo = Bar<string>;

type Foo = Record<string, number>;

type Foo = Readonly<Bar>;

type Foo = Partial<Bar>;

type Foo = Omit<Bar, 'a' | 'b'>;
```

## When Not To Use It

When you can't express some shape with an interface or you need to use a union, tuple type,
Expand Down
17 changes: 17 additions & 0 deletions packages/eslint-plugin/src/rules/no-type-alias.ts
Expand Up @@ -27,6 +27,7 @@ type Options = [
allowLiterals?: Values;
allowMappedTypes?: Values;
allowTupleTypes?: Values;
allowGenerics?: 'always' | 'never';
},
];
type MessageIds = 'noTypeAlias' | 'noCompositionAlias';
Expand Down Expand Up @@ -79,6 +80,9 @@ export default util.createRule<Options, MessageIds>({
allowTupleTypes: {
enum: enumValues,
},
allowGenerics: {
enum: ['always', 'never'],
},
},
additionalProperties: false,
},
Expand All @@ -93,6 +97,7 @@ export default util.createRule<Options, MessageIds>({
allowLiterals: 'never',
allowMappedTypes: 'never',
allowTupleTypes: 'never',
allowGenerics: 'never',
},
],
create(
Expand All @@ -106,6 +111,7 @@ export default util.createRule<Options, MessageIds>({
allowLiterals,
allowMappedTypes,
allowTupleTypes,
allowGenerics,
},
],
) {
Expand Down Expand Up @@ -203,6 +209,13 @@ export default util.createRule<Options, MessageIds>({
return false;
};

const isValidGeneric = (type: TypeWithLabel): boolean => {
return (
type.node.type === AST_NODE_TYPES.TSTypeReference &&
type.node.typeParameters !== undefined
);
};

const checkAndReport = (
optionValue: Values,
isTopLevel: boolean,
Expand Down Expand Up @@ -260,6 +273,10 @@ export default util.createRule<Options, MessageIds>({
} else if (isValidTupleType(type)) {
// tuple types
checkAndReport(allowTupleTypes!, isTopLevel, type, 'Tuple Types');
} else if (isValidGeneric(type)) {
if (allowGenerics === 'never') {
reportError(type.node, type.compositionType, isTopLevel, 'Generics');
}
} else if (
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum
type.node.type.endsWith('Keyword') ||
Expand Down
17 changes: 17 additions & 0 deletions packages/eslint-plugin/tests/rules/no-type-alias.test.ts
Expand Up @@ -481,6 +481,10 @@ type KeyNames = keyof typeof SCALARS;
code: 'type Foo = new (bar: number) => string | null;',
options: [{ allowConstructors: 'always' }],
},
{
code: 'type Foo = Record<string, number>;',
options: [{ allowGenerics: 'always' }],
},
],
invalid: [
{
Expand Down Expand Up @@ -3329,5 +3333,18 @@ type Foo<T> = {
},
],
},
{
code: 'type Foo = Record<string, number>;',
errors: [
{
messageId: 'noTypeAlias',
data: {
alias: 'generics',
},
line: 1,
column: 12,
},
],
},
],
});