mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
feat(linter): add typescript/no-generated-empty-object-type (#26958)
Adds `typescript/no-generated-empty-object-type` as a suspicious type-aware rule, reporting type operations such as `Pick<Data, never>` that resolve to `{}`. Type analysis stays in TSGolint; the rule has no options, fixes, or suggestions.
Requires a backend containing https://github.com/oxc-project/tsgolint/pull/1229. The shared type-aware snapshots were validated against a local build of that implementation. This PR leaves the pinned backend package unchanged.
This commit is contained in:
1 parent
88a0096214
commit
e05b155a6d
13 files changed
+788
-624
No files matched your search
@@ -14,6 +14,7 @@
|
||||
"typescript/no-duplicate-type-constituents": "error",
|
||||
"typescript/no-floating-promises": "error",
|
||||
"typescript/no-for-in-array": "error",
|
||||
"typescript/no-generated-empty-object-type": "error",
|
||||
"typescript/no-implied-eval": "error",
|
||||
"typescript/no-meaningless-void-operator": "error",
|
||||
"typescript/no-misused-spread": "error",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
type Data = { name: string; value: number };
|
||||
// These operations resolve to the empty object type.
|
||||
type EmptyOmit = Omit<Data, "name" | "value">;
|
||||
type EmptyPick = Pick<Data, never>;
|
||||
type EmptyNonNullable = NonNullable<unknown>;
|
||||
type EmptyIntersection = Omit<Data, "name" | "value"> & Omit<Data, "name" | "value">;
|
||||
// These operations retain properties.
|
||||
type WithValue = Omit<Data, "name">;
|
||||
type WithOther = Omit<Data, "name" | "value"> & { other: string };
|
||||
|
||||
type Keys<T> = T extends infer U ? keyof U : never;
|
||||
type Mapped<T extends object> = { [Key in Keys<T>]: Key };
|
||||
type Referenced<T extends object> = Mapped<T>;
|
||||
|
||||
export {};
|
||||
@@ -1457,6 +1457,7 @@ export interface DummyRuleMap {
|
||||
"typescript/no-extraneous-class"?: RuleNoConfig | [AllowWarnDeny, NoExtraneousClass];
|
||||
"typescript/no-floating-promises"?: RuleNoConfig | [AllowWarnDeny, NoFloatingPromisesConfig];
|
||||
"typescript/no-for-in-array"?: RuleNoConfig;
|
||||
"typescript/no-generated-empty-object-type"?: RuleNoConfig;
|
||||
"typescript/no-implied-eval"?: RuleNoConfig;
|
||||
"typescript/no-import-type-side-effects"?: RuleNoConfig;
|
||||
"typescript/no-inferrable-types"?: RuleNoConfig | [AllowWarnDeny, NoInferrableTypes];
|
||||
|
||||
@@ -6,8 +6,8 @@ arguments: --type-aware --silent
|
||||
working directory: fixtures/cli/tsgolint
|
||||
----------
|
||||
|
||||
Found 0 warnings and 81 errors.
|
||||
Finished in <variable>ms on 60 files with 59 rules using 1 threads.
|
||||
Found 0 warnings and 86 errors.
|
||||
Finished in <variable>ms on 61 files with 60 rules using 1 threads.
|
||||
----------
|
||||
CLI result: LintFoundErrors
|
||||
----------
|
||||
+1
-1
@@ -42,7 +42,7 @@ working directory: fixtures/cli/tsgolint
|
||||
help: Remove the debugger statement
|
||||
|
||||
Found 2 warnings and 2 errors.
|
||||
Finished in <variable>ms on 60 files with 2 rules using 1 threads.
|
||||
Finished in <variable>ms on 61 files with 2 rules using 1 threads.
|
||||
----------
|
||||
CLI result: LintFoundErrors
|
||||
----------
|
||||
+1
-1
@@ -16,7 +16,7 @@ working directory: fixtures/cli/tsgolint
|
||||
help: Remove the debugger statement
|
||||
|
||||
Found 0 warnings and 1 error.
|
||||
Finished in <variable>ms on 1 file with 59 rules using 1 threads.
|
||||
Finished in <variable>ms on 1 file with 60 rules using 1 threads.
|
||||
----------
|
||||
CLI result: LintFoundErrors
|
||||
----------
|
||||
@@ -113,6 +113,47 @@ working directory: fixtures/cli/tsgolint
|
||||
`----
|
||||
help: Use a more robust iteration method such as for-of or array.forEach instead.
|
||||
|
||||
x typescript(no-generated-empty-object-type): This type resolves to `{}`, the empty object type. This was likely not intentional.
|
||||
,-[no-generated-empty-object-type.ts:3:18]
|
||||
2 | // These operations resolve to the empty object type.
|
||||
3 | type EmptyOmit = Omit<Data, "name" | "value">;
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
4 | type EmptyPick = Pick<Data, never>;
|
||||
`----
|
||||
|
||||
x typescript(no-generated-empty-object-type): This type resolves to `{}`, the empty object type. This was likely not intentional.
|
||||
,-[no-generated-empty-object-type.ts:4:18]
|
||||
3 | type EmptyOmit = Omit<Data, "name" | "value">;
|
||||
4 | type EmptyPick = Pick<Data, never>;
|
||||
: ^^^^^^^^^^^^^^^^^
|
||||
5 | type EmptyNonNullable = NonNullable<unknown>;
|
||||
`----
|
||||
|
||||
x typescript(no-generated-empty-object-type): This type resolves to `{}`, the empty object type. This was likely not intentional.
|
||||
,-[no-generated-empty-object-type.ts:5:25]
|
||||
4 | type EmptyPick = Pick<Data, never>;
|
||||
5 | type EmptyNonNullable = NonNullable<unknown>;
|
||||
: ^^^^^^^^^^^^^^^^^^^^
|
||||
6 | type EmptyIntersection = Omit<Data, "name" | "value"> & Omit<Data, "name" | "value">;
|
||||
`----
|
||||
|
||||
x typescript(no-duplicate-type-constituents): Intersection type constituent is duplicated with Omit<Data, "name" | "value">.
|
||||
,-[no-generated-empty-object-type.ts:6:57]
|
||||
5 | type EmptyNonNullable = NonNullable<unknown>;
|
||||
6 | type EmptyIntersection = Omit<Data, "name" | "value"> & Omit<Data, "name" | "value">;
|
||||
: ^^^^^^^^^^^^^^|^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
: `-- Type 'Omit<Data, "name" | "value">' is first declared here.
|
||||
7 | // These operations retain properties.
|
||||
`----
|
||||
|
||||
x typescript(no-generated-empty-object-type): This type resolves to `{}`, the empty object type. This was likely not intentional.
|
||||
,-[no-generated-empty-object-type.ts:6:26]
|
||||
5 | type EmptyNonNullable = NonNullable<unknown>;
|
||||
6 | type EmptyIntersection = Omit<Data, "name" | "value"> & Omit<Data, "name" | "value">;
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
7 | // These operations retain properties.
|
||||
`----
|
||||
|
||||
x typescript(no-implied-eval): Implied eval.
|
||||
,-[no-implied-eval.ts:1:12]
|
||||
1 | setTimeout('alert("Hi!");', 100);
|
||||
@@ -694,8 +735,8 @@ working directory: fixtures/cli/tsgolint
|
||||
`----
|
||||
help: If your function does not access `this`, you can annotate it with `this: void`, or consider using an arrow function instead.
|
||||
|
||||
Found 0 warnings and 81 errors.
|
||||
Finished in <variable>ms on 60 files with 59 rules using 1 threads.
|
||||
Found 0 warnings and 86 errors.
|
||||
Finished in <variable>ms on 61 files with 60 rules using 1 threads.
|
||||
----------
|
||||
CLI result: LintFoundErrors
|
||||
----------
|
||||
+1
-1
@@ -42,7 +42,7 @@ working directory: fixtures/cli/tsgolint
|
||||
help: Remove the debugger statement
|
||||
|
||||
Found 2 warnings and 2 errors.
|
||||
Finished in <variable>ms on 60 files with 2 rules using 1 threads.
|
||||
Finished in <variable>ms on 61 files with 2 rules using 1 threads.
|
||||
----------
|
||||
CLI result: LintFoundErrors
|
||||
----------
|
||||
@@ -1748,6 +1748,13 @@ impl RuleRunner for crate::rules::typescript::no_for_in_array::NoForInArray {
|
||||
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown;
|
||||
}
|
||||
|
||||
impl RuleRunner
|
||||
for crate::rules::typescript::no_generated_empty_object_type::NoGeneratedEmptyObjectType
|
||||
{
|
||||
const NODE_TYPES: Option<&AstTypesBitset> = None;
|
||||
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown;
|
||||
}
|
||||
|
||||
impl RuleRunner for crate::rules::typescript::no_implied_eval::NoImpliedEval {
|
||||
const NODE_TYPES: Option<&AstTypesBitset> = None;
|
||||
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown;
|
||||
|
||||
+661
-617
File diff suppressed because it is too large.
Load diff
@@ -267,6 +267,7 @@ pub(crate) mod typescript {
|
||||
pub mod no_extraneous_class;
|
||||
pub mod no_floating_promises;
|
||||
pub mod no_for_in_array;
|
||||
pub mod no_generated_empty_object_type;
|
||||
pub mod no_implied_eval;
|
||||
pub mod no_import_type_side_effects;
|
||||
pub mod no_inferrable_types;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
|
||||
use crate::rule::Rule;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NoGeneratedEmptyObjectType;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
///
|
||||
/// Disallows type operations that resolve to the empty object type `{}`.
|
||||
/// This includes generic type references and intersections, but excludes
|
||||
/// mapped types whose keys have not yet been resolved.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// The empty object type `{}` accepts any non-nullish value, including
|
||||
/// primitives such as strings and numbers. Accidentally producing this type
|
||||
/// with a utility type can therefore allow values that were not intended.
|
||||
///
|
||||
/// This rule checks generated types. Use `typescript/no-empty-object-type`
|
||||
/// to disallow explicitly written empty object types.
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// Examples of **incorrect** code for this rule:
|
||||
/// ```ts
|
||||
/// type Data = { name: string; value: number };
|
||||
/// type Empty = Omit<Data, 'name' | 'value'>;
|
||||
/// type NoProperties = Pick<Data, never>;
|
||||
/// type NonNullish = NonNullable<unknown>;
|
||||
/// ```
|
||||
///
|
||||
/// Examples of **correct** code for this rule:
|
||||
/// ```ts
|
||||
/// type Data = { name: string; value: number };
|
||||
/// type WithValue = Omit<Data, 'name'>;
|
||||
/// type WithOther = Omit<Data, 'name' | 'value'> & { other: string };
|
||||
///
|
||||
/// type Keys<T> = T extends infer U ? keyof U : never;
|
||||
/// type Mapped<T extends object> = { [Key in Keys<T>]: Key };
|
||||
/// type Referenced<T extends object> = Mapped<T>;
|
||||
/// ```
|
||||
NoGeneratedEmptyObjectType(tsgolint),
|
||||
typescript,
|
||||
suspicious,
|
||||
version = "next",
|
||||
short_description = "Disallow type operations that resolve to the empty object type.",
|
||||
);
|
||||
|
||||
impl Rule for NoGeneratedEmptyObjectType {}
|
||||
@@ -8042,6 +8042,9 @@
|
||||
"typescript/no-for-in-array": {
|
||||
"$ref": "#/definitions/RuleNoConfig"
|
||||
},
|
||||
"typescript/no-generated-empty-object-type": {
|
||||
"$ref": "#/definitions/RuleNoConfig"
|
||||
},
|
||||
"typescript/no-implied-eval": {
|
||||
"$ref": "#/definitions/RuleNoConfig"
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user