diff --git a/lib/utils/ts-ast-utils.js b/lib/utils/ts-ast-utils.js index e7cc52420..330e6b985 100644 --- a/lib/utils/ts-ast-utils.js +++ b/lib/utils/ts-ast-utils.js @@ -10,6 +10,7 @@ const { findVariable } = require('@eslint-community/eslint-utils') /** * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeProp} ComponentTypeProp * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeEmit} ComponentTypeEmit + * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentUnknownEmit} ComponentUnknownEmit */ module.exports = { @@ -147,22 +148,42 @@ function* extractRuntimeProps(context, node) { /** * @see https://github.com/vuejs/vue-next/blob/348c3b01e56383ffa70b180d1376fdf4ac12e274/packages/compiler-sfc/src/compileScript.ts#L1632 * @param {TSTypeLiteral | TSInterfaceBody | TSFunctionType} node - * @returns {IterableIterator} + * @returns {IterableIterator} */ function* extractRuntimeEmits(node) { - if (node.type === 'TSTypeLiteral' || node.type === 'TSInterfaceBody') { - const members = node.type === 'TSTypeLiteral' ? node.members : node.body - for (const t of members) { - if (t.type === 'TSCallSignatureDeclaration') { - yield* extractEventNames( - t.params[0], - /** @type {TSCallSignatureDeclaration} */ (t) - ) + if (node.type === 'TSFunctionType') { + yield* extractEventNames(node.params[0], node) + return + } + const members = node.type === 'TSTypeLiteral' ? node.members : node.body + for (const member of members) { + if (member.type === 'TSCallSignatureDeclaration') { + yield* extractEventNames( + member.params[0], + /** @type {TSCallSignatureDeclaration} */ (member) + ) + } else if ( + member.type === 'TSPropertySignature' || + member.type === 'TSMethodSignature' + ) { + if (member.key.type !== 'Identifier' && member.key.type !== 'Literal') { + yield { + type: 'unknown', + node: member.key + } + continue + } + yield { + type: 'type', + key: /** @type {Identifier | Literal} */ (member.key), + emitName: + member.key.type === 'Identifier' + ? member.key.name + : `${member.key.value}`, + value: null, + node: /** @type {TSPropertySignature | TSMethodSignature} */ (member) } } - return - } else { - yield* extractEventNames(node.params[0], node) } } diff --git a/tests/lib/rules/require-explicit-emits.js b/tests/lib/rules/require-explicit-emits.js index 9a22c74a9..a5bc5114c 100644 --- a/tests/lib/rules/require-explicit-emits.js +++ b/tests/lib/rules/require-explicit-emits.js @@ -593,6 +593,31 @@ tester.run('require-explicit-emits', rule, { `, options: [{ allowProps: true }] + }, + { + // new syntax in Vue 3.3 + filename: 'test.vue', + code: ` + + `, + parserOptions: { parser: require.resolve('@typescript-eslint/parser') } + }, + { + // new syntax in Vue 3.3 + filename: 'test.vue', + code: ` + + `, + parserOptions: { parser: require.resolve('@typescript-eslint/parser') } } ], invalid: [ @@ -1886,6 +1911,45 @@ emits: {'foo': null} ] } ] + }, + { + // new syntax in Vue 3.3 + filename: 'test.vue', + code: ` + + `, + parserOptions: { parser: require.resolve('@typescript-eslint/parser') }, + errors: [ + { + message: + 'The "bar" event has been triggered but not declared on `defineEmits`.', + line: 5 + } + ] + }, + { + // new syntax in Vue 3.3 + filename: 'test.vue', + code: ` + + `, + parserOptions: { parser: require.resolve('@typescript-eslint/parser') }, + errors: [ + { + message: + 'The "bar" event has been triggered but not declared on `defineEmits`.', + line: 6 + } + ] } ] }) diff --git a/typings/eslint-plugin-vue/util-types/utils.ts b/typings/eslint-plugin-vue/util-types/utils.ts index d998761be..ef76b6cdb 100644 --- a/typings/eslint-plugin-vue/util-types/utils.ts +++ b/typings/eslint-plugin-vue/util-types/utils.ts @@ -152,13 +152,23 @@ export type ComponentUnknownEmit = { node: Expression | SpreadElement | null } -export type ComponentTypeEmit = { +export type ComponentTypeEmitCallSignature = { type: 'type' key: TSLiteralType emitName: string value: null node: TSCallSignatureDeclaration | TSFunctionType } +export type ComponentTypeEmitPropertySignature = { + type: 'type' + key: Identifier | Literal + emitName: string + value: null + node: TSPropertySignature | TSMethodSignature +} +export type ComponentTypeEmit = + | ComponentTypeEmitCallSignature + | ComponentTypeEmitPropertySignature export type ComponentEmit = | ComponentArrayEmit