diff --git a/.changeset/strong-wombats-worry.md b/.changeset/strong-wombats-worry.md
new file mode 100644
index 000000000..aa6742bea
--- /dev/null
+++ b/.changeset/strong-wombats-worry.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-svelte": minor
+---
+
+feat: added the `svelte/experimental-require-slot-types` rule
diff --git a/README.md b/README.md
index 1919fcf15..4ae387077 100644
--- a/README.md
+++ b/README.md
@@ -379,6 +379,7 @@ These rules extend the rules provided by ESLint itself, or other plugins to work
| Rule ID | Description | |
|:--------|:------------|:---|
+| [svelte/experimental-require-slot-types](https://ota-meshi.github.io/eslint-plugin-svelte/rules/experimental-require-slot-types/) | require slot type declaration using the `$$Slots` interface | |
| [svelte/experimental-require-strict-events](https://ota-meshi.github.io/eslint-plugin-svelte/rules/experimental-require-strict-events/) | require the strictEvents attribute on `
+
+No slots here!
+```
+
+
+
+
+
+
+
+```svelte
+
+
+
+
+```
+
+
+
+
+
+
+
+```svelte
+
+
+
+
+```
+
+
+
+
+
+
+
+```svelte
+
+
+
+
+```
+
+
+
+
+
+
+
+```svelte
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/experimental-require-slot-types.ts)
+- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/experimental-require-slot-types.ts)
diff --git a/src/rules/experimental-require-slot-types.ts b/src/rules/experimental-require-slot-types.ts
new file mode 100644
index 000000000..32f31ae5c
--- /dev/null
+++ b/src/rules/experimental-require-slot-types.ts
@@ -0,0 +1,50 @@
+import { createRule } from "../utils"
+import { getLangValue } from "../utils/ast-utils"
+
+export default createRule("experimental-require-slot-types", {
+ meta: {
+ docs: {
+ description:
+ "require slot type declaration using the `$$Slots` interface",
+ category: "Experimental",
+ recommended: false,
+ },
+ schema: [],
+ messages: {
+ missingSlotsInterface: `The component must define the $$Slots interface.`,
+ },
+ type: "suggestion",
+ },
+ create(context) {
+ let isTs = false
+ let hasSlot = false
+ let hasInterface = false
+ return {
+ SvelteScriptElement(node) {
+ const lang = getLangValue(node)?.toLowerCase()
+ isTs = lang === "ts" || lang === "typescript"
+ },
+ SvelteElement(node) {
+ if (node.name.type === "SvelteName" && node.name.name === "slot") {
+ hasSlot = true
+ }
+ },
+ TSInterfaceDeclaration(node) {
+ if (node.id.name === "$$Slots") {
+ hasInterface = true
+ }
+ },
+ "Program:exit"() {
+ if (isTs && hasSlot && !hasInterface) {
+ context.report({
+ loc: {
+ line: 1,
+ column: 1,
+ },
+ messageId: "missingSlotsInterface",
+ })
+ }
+ },
+ }
+ },
+})
diff --git a/src/utils/rules.ts b/src/utils/rules.ts
index 5d051d4ba..bc8c4813c 100644
--- a/src/utils/rules.ts
+++ b/src/utils/rules.ts
@@ -3,6 +3,7 @@ import typescriptEslintNoUnnecessaryCondition from "../rules/@typescript-eslint/
import buttonHasType from "../rules/button-has-type"
import commentDirective from "../rules/comment-directive"
import derivedHasSameInputsOutputs from "../rules/derived-has-same-inputs-outputs"
+import experimentalRequireSlotTypes from "../rules/experimental-require-slot-types"
import experimentalRequireStrictEvents from "../rules/experimental-require-strict-events"
import firstAttributeLinebreak from "../rules/first-attribute-linebreak"
import htmlClosingBracketSpacing from "../rules/html-closing-bracket-spacing"
@@ -56,6 +57,7 @@ export const rules = [
buttonHasType,
commentDirective,
derivedHasSameInputsOutputs,
+ experimentalRequireSlotTypes,
experimentalRequireStrictEvents,
firstAttributeLinebreak,
htmlClosingBracketSpacing,
diff --git a/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-errors.yaml b/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-errors.yaml
new file mode 100644
index 000000000..628f8ecb1
--- /dev/null
+++ b/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-errors.yaml
@@ -0,0 +1,4 @@
+- message: The component must define the $$Slots interface.
+ line: 1
+ column: 2
+ suggestions: null
diff --git a/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-input.svelte
new file mode 100644
index 000000000..2c02c7ef5
--- /dev/null
+++ b/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-input.svelte
@@ -0,0 +1,4 @@
+
+
+
diff --git a/tests/fixtures/rules/experimental-require-slot-types/valid/has-slot-types01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/valid/has-slot-types01-input.svelte
new file mode 100644
index 000000000..c902e4a06
--- /dev/null
+++ b/tests/fixtures/rules/experimental-require-slot-types/valid/has-slot-types01-input.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/tests/fixtures/rules/experimental-require-slot-types/valid/named-slot01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/valid/named-slot01-input.svelte
new file mode 100644
index 000000000..ac2713e44
--- /dev/null
+++ b/tests/fixtures/rules/experimental-require-slot-types/valid/named-slot01-input.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/tests/fixtures/rules/experimental-require-slot-types/valid/no-slots01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/valid/no-slots01-input.svelte
new file mode 100644
index 000000000..d0857d009
--- /dev/null
+++ b/tests/fixtures/rules/experimental-require-slot-types/valid/no-slots01-input.svelte
@@ -0,0 +1,4 @@
+
+
+content
diff --git a/tests/fixtures/rules/experimental-require-slot-types/valid/no-typescript01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/valid/no-typescript01-input.svelte
new file mode 100644
index 000000000..9cc1fe581
--- /dev/null
+++ b/tests/fixtures/rules/experimental-require-slot-types/valid/no-typescript01-input.svelte
@@ -0,0 +1,4 @@
+
+
+
diff --git a/tests/src/rules/experimental-require-slot-types.ts b/tests/src/rules/experimental-require-slot-types.ts
new file mode 100644
index 000000000..af9978d93
--- /dev/null
+++ b/tests/src/rules/experimental-require-slot-types.ts
@@ -0,0 +1,16 @@
+import { RuleTester } from "eslint"
+import rule from "../../../src/rules/experimental-require-slot-types"
+import { loadTestCases } from "../../utils/utils"
+
+const tester = new RuleTester({
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: "module",
+ },
+})
+
+tester.run(
+ "experimental-require-slot-types",
+ rule as any,
+ loadTestCases("experimental-require-slot-types"),
+)