From 46e3433b336a0b61e652bff810aeb1ca789891e2 Mon Sep 17 00:00:00 2001 From: auvred Date: Sat, 3 Feb 2024 17:38:22 +0300 Subject: [PATCH 1/3] feat(valid-v-model): add support for type assertions and non-null expressions --- lib/rules/valid-v-model.js | 7 ++++++- tests/lib/rules/valid-v-model.js | 18 ++++++++++++++++++ .../eslint-plugin-vue/util-types/ast/ts-ast.ts | 6 ++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/rules/valid-v-model.js b/lib/rules/valid-v-model.js index 2378c86f2..0a15a4460 100644 --- a/lib/rules/valid-v-model.js +++ b/lib/rules/valid-v-model.js @@ -49,7 +49,12 @@ function isOptionalChainingMemberExpression(node) { * @returns {boolean} `true` if the node can be LHS. */ function isLhs(node) { - return node.type === 'Identifier' || node.type === 'MemberExpression' + return ( + node.type === 'Identifier' || + node.type === 'MemberExpression' || + node.type === 'TSAsExpression' || + node.type === 'TSNonNullExpression' + ) } /** diff --git a/tests/lib/rules/valid-v-model.js b/tests/lib/rules/valid-v-model.js index 38b4ae175..073534684 100644 --- a/tests/lib/rules/valid-v-model.js +++ b/tests/lib/rules/valid-v-model.js @@ -143,6 +143,24 @@ tester.run('valid-v-model', rule, { { filename: 'comment-value.vue', code: '' + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + } + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + } } ], invalid: [ diff --git a/typings/eslint-plugin-vue/util-types/ast/ts-ast.ts b/typings/eslint-plugin-vue/util-types/ast/ts-ast.ts index e3c5f0faf..fec18e9e8 100644 --- a/typings/eslint-plugin-vue/util-types/ast/ts-ast.ts +++ b/typings/eslint-plugin-vue/util-types/ast/ts-ast.ts @@ -6,6 +6,7 @@ import * as ES from './es-ast' import { TSESTree } from '@typescript-eslint/types' export type TSNode = | TSAsExpression + | TSNonNullExpression | TSTypeParameterInstantiation | TSPropertySignature | TSMethodSignatureBase @@ -20,6 +21,11 @@ export interface TSAsExpression extends HasParentNode { typeAnnotation: any } +export interface TSNonNullExpression extends HasParentNode { + type: 'TSNonNullExpression' + expression: ES.Expression +} + export interface TSTypeParameterInstantiation extends HasParentNode { type: 'TSTypeParameterInstantiation' params: TypeNode[] From 8ddc35bf9dead2eb22ea196405b262dbc43a8b9e Mon Sep 17 00:00:00 2001 From: auvred Date: Mon, 19 Feb 2024 10:09:24 +0300 Subject: [PATCH 2/3] fix: check if assertion expressoin is valid lhs --- lib/rules/valid-v-model.js | 11 ++-- tests/lib/rules/valid-v-model.js | 87 ++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/lib/rules/valid-v-model.js b/lib/rules/valid-v-model.js index 0a15a4460..d818439bd 100644 --- a/lib/rules/valid-v-model.js +++ b/lib/rules/valid-v-model.js @@ -49,12 +49,11 @@ function isOptionalChainingMemberExpression(node) { * @returns {boolean} `true` if the node can be LHS. */ function isLhs(node) { - return ( - node.type === 'Identifier' || - node.type === 'MemberExpression' || - node.type === 'TSAsExpression' || - node.type === 'TSNonNullExpression' - ) + if (node.type === 'TSAsExpression' || node.type === 'TSNonNullExpression') { + return isLhs(node.expression) + } + + return node.type === 'Identifier' || node.type === 'MemberExpression' } /** diff --git a/tests/lib/rules/valid-v-model.js b/tests/lib/rules/valid-v-model.js index 073534684..7ef63d01e 100644 --- a/tests/lib/rules/valid-v-model.js +++ b/tests/lib/rules/valid-v-model.js @@ -161,6 +161,33 @@ tester.run('valid-v-model', rule, { parser: require.resolve('@typescript-eslint/parser') } } + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + } + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + } + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + } } ], invalid: [ @@ -259,6 +286,66 @@ tester.run('valid-v-model', rule, { filename: 'test.vue', code: '', errors: ["'v-model' directive has potential null object property access."] + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ] + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ] + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ] + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ] + }, + { + filename: 'test.vue', + code: '', + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ] } ] }) From d5dd7bba600c5b98df7fec8d062657256a4c8298 Mon Sep 17 00:00:00 2001 From: auvred Date: Mon, 19 Feb 2024 10:10:23 +0300 Subject: [PATCH 3/3] chore: run lint --fix --- tests/lib/rules/valid-v-model.js | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/lib/rules/valid-v-model.js b/tests/lib/rules/valid-v-model.js index 7ef63d01e..02264e527 100644 --- a/tests/lib/rules/valid-v-model.js +++ b/tests/lib/rules/valid-v-model.js @@ -290,62 +290,62 @@ tester.run('valid-v-model', rule, { { filename: 'test.vue', code: '', + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ], languageOptions: { parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - }, - errors: [ - "'v-model' directives require the attribute value which is valid as LHS." - ] + } }, { filename: 'test.vue', code: '', + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ], languageOptions: { parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - }, - errors: [ - "'v-model' directives require the attribute value which is valid as LHS." - ] + } }, { filename: 'test.vue', code: '', + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ], languageOptions: { parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - }, - errors: [ - "'v-model' directives require the attribute value which is valid as LHS." - ] + } }, { filename: 'test.vue', code: '', + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ], languageOptions: { parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - }, - errors: [ - "'v-model' directives require the attribute value which is valid as LHS." - ] + } }, { filename: 'test.vue', code: '', + errors: [ + "'v-model' directives require the attribute value which is valid as LHS." + ], languageOptions: { parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - }, - errors: [ - "'v-model' directives require the attribute value which is valid as LHS." - ] + } } ] })