From f9e36f17386cd0f14dd6101bf37183e73f37c19a Mon Sep 17 00:00:00 2001 From: Sysix <3897725+Sysix@users.noreply.github.com> Date: Fri, 12 Jun 2026 00:45:30 +0000 Subject: [PATCH] feat(linter): add schema for `typescript/no-duplicate-type-constituents` (#23308) --- .../oxlint/src-js/package/config.generated.ts | 17 ++++++++- crates/oxc_linter/src/utils/schemars.rs | 3 +- npm/oxlint/configuration_schema.json | 37 ++++++++++++++++++- .../src/snapshots/schema_json.snap | 37 ++++++++++++++++++- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/apps/oxlint/src-js/package/config.generated.ts b/apps/oxlint/src-js/package/config.generated.ts index 45f53713b1..ac01a5923e 100644 --- a/apps/oxlint/src-js/package/config.generated.ts +++ b/apps/oxlint/src-js/package/config.generated.ts @@ -1310,7 +1310,10 @@ export interface DummyRuleMap { | [AllowWarnDeny, NoConfusingVoidExpressionConfig]; "typescript/no-deprecated"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, NoDeprecatedConfig]; "typescript/no-duplicate-enum-values"?: RuleNoConfig; - "typescript/no-duplicate-type-constituents"?: DummyRule; + "typescript/no-duplicate-type-constituents"?: + | AllowWarnDeny + | [AllowWarnDeny] + | [AllowWarnDeny, NoDuplicateTypeConstituentsConfig]; "typescript/no-dynamic-delete"?: RuleNoConfig; "typescript/no-empty-interface"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, NoEmptyInterface]; "typescript/no-empty-object-type"?: DummyRule; @@ -4698,6 +4701,18 @@ export interface PackageSpecifier { */ package: string; } +export interface NoDuplicateTypeConstituentsConfig { + /** + * Whether to ignore duplicate types in intersection types. + * When true, allows `type T = A & A`. + */ + ignoreIntersections?: boolean; + /** + * Whether to ignore duplicate types in union types. + * When true, allows `type T = A | A`. + */ + ignoreUnions?: boolean; +} export interface NoEmptyInterface { /** * When set to `true`, allows empty interfaces that extend a single interface. diff --git a/crates/oxc_linter/src/utils/schemars.rs b/crates/oxc_linter/src/utils/schemars.rs index 19b210fbbe..e02f03d186 100644 --- a/crates/oxc_linter/src/utils/schemars.rs +++ b/crates/oxc_linter/src/utils/schemars.rs @@ -13,7 +13,7 @@ use crate::rules::RuleEnum; /// These should return the DummyRule struct/schema instead of the actual rule struct/schema, /// until we verify that the rule's schema is valid and can be generated by schemars. #[cfg(feature = "ruledocs")] -const NO_VERIFIED_VALID_SCHEMA: [&str; 42] = [ +const NO_VERIFIED_VALID_SCHEMA: [&str; 41] = [ "eslint/func-name-matching", "eslint/no-restricted-globals", "eslint/no-restricted-imports", @@ -48,7 +48,6 @@ const NO_VERIFIED_VALID_SCHEMA: [&str; 42] = [ "typescript/consistent-type-assertions", "typescript/consistent-type-imports", "typescript/no-empty-object-type", - "typescript/no-duplicate-type-constituents", "typescript/no-restricted-types", "unicorn/filename-case", "unicorn/import-style", diff --git a/npm/oxlint/configuration_schema.json b/npm/oxlint/configuration_schema.json index ee1c6078d3..42ef7487c9 100644 --- a/npm/oxlint/configuration_schema.json +++ b/npm/oxlint/configuration_schema.json @@ -6402,7 +6402,24 @@ "$ref": "#/definitions/RuleNoConfig" }, "typescript/no-duplicate-type-constituents": { - "$ref": "#/definitions/DummyRule" + "anyOf": [ + { + "$ref": "#/definitions/AllowWarnDeny" + }, + { + "type": "array", + "items": [ + { + "$ref": "#/definitions/AllowWarnDeny" + }, + { + "$ref": "#/definitions/NoDuplicateTypeConstituentsConfig" + } + ], + "maxItems": 2, + "minItems": 1 + } + ] }, "typescript/no-dynamic-delete": { "$ref": "#/definitions/RuleNoConfig" @@ -11456,6 +11473,24 @@ }, "additionalProperties": false }, + "NoDuplicateTypeConstituentsConfig": { + "type": "object", + "properties": { + "ignoreIntersections": { + "description": "Whether to ignore duplicate types in intersection types.\nWhen true, allows `type T = A & A`.", + "default": false, + "type": "boolean", + "markdownDescription": "Whether to ignore duplicate types in intersection types.\nWhen true, allows `type T = A & A`." + }, + "ignoreUnions": { + "description": "Whether to ignore duplicate types in union types.\nWhen true, allows `type T = A | A`.", + "default": false, + "type": "boolean", + "markdownDescription": "Whether to ignore duplicate types in union types.\nWhen true, allows `type T = A | A`." + } + }, + "additionalProperties": false + }, "NoDuplicates": { "type": "object", "properties": { diff --git a/tasks/website_linter/src/snapshots/schema_json.snap b/tasks/website_linter/src/snapshots/schema_json.snap index 8a31f22fb4..69440504d1 100644 --- a/tasks/website_linter/src/snapshots/schema_json.snap +++ b/tasks/website_linter/src/snapshots/schema_json.snap @@ -6406,7 +6406,24 @@ expression: json "$ref": "#/definitions/RuleNoConfig" }, "typescript/no-duplicate-type-constituents": { - "$ref": "#/definitions/DummyRule" + "anyOf": [ + { + "$ref": "#/definitions/AllowWarnDeny" + }, + { + "type": "array", + "items": [ + { + "$ref": "#/definitions/AllowWarnDeny" + }, + { + "$ref": "#/definitions/NoDuplicateTypeConstituentsConfig" + } + ], + "maxItems": 2, + "minItems": 1 + } + ] }, "typescript/no-dynamic-delete": { "$ref": "#/definitions/RuleNoConfig" @@ -11460,6 +11477,24 @@ expression: json }, "additionalProperties": false }, + "NoDuplicateTypeConstituentsConfig": { + "type": "object", + "properties": { + "ignoreIntersections": { + "description": "Whether to ignore duplicate types in intersection types.\nWhen true, allows `type T = A & A`.", + "default": false, + "type": "boolean", + "markdownDescription": "Whether to ignore duplicate types in intersection types.\nWhen true, allows `type T = A & A`." + }, + "ignoreUnions": { + "description": "Whether to ignore duplicate types in union types.\nWhen true, allows `type T = A | A`.", + "default": false, + "type": "boolean", + "markdownDescription": "Whether to ignore duplicate types in union types.\nWhen true, allows `type T = A | A`." + } + }, + "additionalProperties": false + }, "NoDuplicates": { "type": "object", "properties": {