diff --git a/docs/rules/define-emits-declaration.md b/docs/rules/define-emits-declaration.md index 2fd4b478f..7a11524c5 100644 --- a/docs/rules/define-emits-declaration.md +++ b/docs/rules/define-emits-declaration.md @@ -5,13 +5,15 @@ title: vue/define-emits-declaration description: enforce declaration style of `defineEmits` since: v9.5.0 --- + # vue/define-emits-declaration > enforce declaration style of `defineEmits` ## :book: Rule Details -This rule enforces `defineEmits` typing style which you should use `type-based` or `runtime` declaration. +This rule enforces `defineEmits` typing style which you should use `type-based`, strict `type-literal` +(introduced in Vue 3.3), or `runtime` declaration. This rule only works in setup script and `lang="ts"`. @@ -25,6 +27,12 @@ const emit = defineEmits<{ (e: 'update', value: string): void }>() +/* ✓ GOOD */ +const emit = defineEmits<{ + change: [id: number] + update: [value: string] +}>() + /* ✗ BAD */ const emit = defineEmits({ change: (id) => typeof id == 'number', @@ -41,10 +49,11 @@ const emit = defineEmits(['change', 'update']) ## :wrench: Options ```json - "vue/define-emits-declaration": ["error", "type-based" | "runtime"] + "vue/define-emits-declaration": ["error", "type-based" | "type-literal" | "runtime"] ``` -- `type-based` (default) enforces type-based declaration +- `type-based` (default) enforces type based declaration +- `type-literal` enforces strict "type literal" type based declaration - `runtime` enforces runtime declaration ### `runtime` @@ -72,6 +81,37 @@ const emit = defineEmits(['change', 'update']) +### `type-literal` + + + +```vue + +``` + + + ## :couple: Related Rules - [vue/define-props-declaration](./define-props-declaration.md) diff --git a/lib/rules/define-emits-declaration.js b/lib/rules/define-emits-declaration.js index 478e1d840..d17e76537 100644 --- a/lib/rules/define-emits-declaration.js +++ b/lib/rules/define-emits-declaration.js @@ -6,6 +6,11 @@ const utils = require('../utils') +/** + * @typedef {import('@typescript-eslint/types').TSESTree.TSTypeLiteral} TSTypeLiteral + * + */ + module.exports = { meta: { type: 'suggestion', @@ -17,12 +22,14 @@ module.exports = { fixable: null, schema: [ { - enum: ['type-based', 'runtime'] + enum: ['type-based', 'type-literal', 'runtime'] } ], messages: { - hasArg: 'Use type-based declaration instead of runtime declaration.', - hasTypeArg: 'Use runtime declaration instead of type-based declaration.' + hasArg: 'Use type based declaration instead of runtime declaration.', + hasTypeArg: 'Use runtime declaration instead of type based declaration.', + hasTypeCallArg: + 'Use new type literal declaration instead of the old call signature declaration.' } }, /** @param {RuleContext} context */ @@ -46,6 +53,28 @@ module.exports = { break } + case 'type-literal': { + if (node.arguments.length > 0) { + context.report({ + node, + messageId: 'hasArg' + }) + return + } + + const typeArguments = node.typeArguments || node.typeParameters + const param = /** @type {TSTypeLiteral} */ (typeArguments.params[0]) + for (const memberNode of param.members) { + if (memberNode.type !== 'TSPropertySignature') { + context.report({ + node: memberNode, + messageId: 'hasTypeCallArg' + }) + } + } + break + } + case 'runtime': { const typeArguments = node.typeArguments || node.typeParameters if (typeArguments && typeArguments.params.length > 0) { diff --git a/tests/lib/rules/define-emits-declaration.js b/tests/lib/rules/define-emits-declaration.js index 372fa1a4c..43ecb385b 100644 --- a/tests/lib/rules/define-emits-declaration.js +++ b/tests/lib/rules/define-emits-declaration.js @@ -63,6 +63,36 @@ tester.run('define-emits-declaration', rule, { `, options: ['runtime'] }, + { + filename: 'test.vue', + code: ` + + `, + options: ['type-based'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, + { + filename: 'test.vue', + code: ` + + `, + options: ['type-literal'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, { filename: 'test.vue', // ignore code without defineEmits @@ -82,7 +112,7 @@ tester.run('define-emits-declaration', rule, { code: ` + `, + options: ['type-literal'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + }, + errors: [ + { + message: 'Use type based declaration instead of runtime declaration.', line: 3 } ] @@ -142,10 +190,59 @@ tester.run('define-emits-declaration', rule, { }, errors: [ { - message: 'Use runtime declaration instead of type-based declaration.', + message: 'Use runtime declaration instead of type based declaration.', line: 3 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: ['type-literal'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + }, + errors: [ + { + message: + 'Use new type literal declaration instead of the old call signature declaration.', + line: 4 + }, + { + message: + 'Use new type literal declaration instead of the old call signature declaration.', + line: 5 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: ['type-literal'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + }, + errors: [ + { + message: + 'Use new type literal declaration instead of the old call signature declaration.', + line: 5 + } + ] } ] })