diff --git a/crates/oxc_linter/src/context/mod.rs b/crates/oxc_linter/src/context/mod.rs
index ef2e45964152a..2f91e967bba71 100644
--- a/crates/oxc_linter/src/context/mod.rs
+++ b/crates/oxc_linter/src/context/mod.rs
@@ -17,6 +17,7 @@ use crate::{
config::GlobalValue,
disable_directives::DisableDirectives,
fixer::{Fix, FixKind, Message, PossibleFixes, RuleFix, RuleFixer},
+ frameworks::FrameworkOptions,
};
mod host;
@@ -443,6 +444,12 @@ impl<'a> LintContext<'a> {
self.parent.frameworks
}
+ /// Returns the framework options for the current script block.
+ /// For Vue files, this can be `FrameworkOptions::VueSetup` if we're in a `
+ /// ```
+ ///
+ /// Examples of **correct** code for this rule:
+ /// ```vue
+ ///
+ /// ```
+ NoRequiredPropWithDefault,
+ vue,
+ suspicious,
+ pending
+);
+
+impl Rule for NoRequiredPropWithDefault {
+ fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ let is_vue = ctx.file_path().extension().is_some_and(|ext| ext == "vue");
+ if is_vue {
+ self.run_on_vue(node, ctx);
+ } else {
+ self.check_define_component(node, ctx);
+ }
+ }
+}
+
+impl NoRequiredPropWithDefault {
+ fn run_on_vue<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ if ctx.frameworks_options() == FrameworkOptions::VueSetup {
+ self.run_on_setup(node, ctx);
+ } else {
+ self.run_on_composition(node, ctx);
+ }
+ }
+
+ #[expect(clippy::unused_self)]
+ fn check_define_component(&self, node: &AstNode<'_>, ctx: &LintContext<'_>) {
+ // only check `defineComponent` method
+ // e.g. `let component = defineComponent({ props: { name: { required: true, default: 'a' } } })`
+ let AstKind::CallExpression(call_expr) = node.kind() else {
+ return;
+ };
+ let Some(ident) = call_expr.callee.get_identifier_reference() else {
+ return;
+ };
+ if ident.name.as_str() == "defineComponent" && call_expr.arguments.len() == 1 {
+ let arg = &call_expr.arguments[0];
+ let Some(Expression::ObjectExpression(obj)) = arg.as_expression() else {
+ return;
+ };
+ handle_object_expression(ctx, obj);
+ }
+ }
+
+ fn run_on_setup<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ let AstKind::CallExpression(call_expr) = node.kind() else {
+ return;
+ };
+ let Some(ident) = call_expr.callee.get_identifier_reference() else {
+ return;
+ };
+
+ match ident.name.as_str() {
+ "defineProps" => {
+ if let Some(arge) = call_expr.arguments.first() {
+ let Some(Expression::ObjectExpression(obj)) = arge.as_expression() else {
+ return;
+ };
+ // Here we need to consider the following two examples
+ // 1. const props = defineProps({ name: { required: true, default: 'a' } })
+ // 2. const { name = 'a' } = defineProps({ name: { required: true } })
+ let key_hash =
+ collect_hash_from_variable_declarator(ctx, node).unwrap_or_default();
+ handle_prop_object(ctx, obj, Some(&key_hash));
+ }
+ if call_expr.arguments.is_empty() {
+ // if `defineProps` is used without arguments, we need to check the type arguments
+ // e.g. `const { name = 'a' } = defineProps()`
+ let Some(type_args) = &call_expr.type_arguments else {
+ return;
+ };
+ let Some(first_type_argument) = type_args.params.first() else {
+ return;
+ };
+ if let Some(key_hash) = collect_hash_from_variable_declarator(ctx, node) {
+ handle_type_argument(ctx, first_type_argument, &key_hash);
+ }
+ }
+ }
+ "withDefaults" if call_expr.arguments.len() == 2 => {
+ let [first_arg, second_arg] = call_expr.arguments.as_slice() else {
+ return;
+ };
+ if let (Some(first_arg_expr), Some(second_arg_expr)) =
+ (first_arg.as_expression(), second_arg.as_expression())
+ {
+ let Expression::ObjectExpression(second_obj_expr) =
+ second_arg_expr.get_inner_expression()
+ else {
+ return;
+ };
+ let Some(key_hash) = collect_hash_from_object_expr(second_obj_expr) else {
+ return;
+ };
+ process_define_props_call(ctx, first_arg_expr, &key_hash);
+ }
+ }
+ _ => {
+ self.check_define_component(node, ctx);
+ }
+ }
+ }
+
+ fn run_on_composition<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ match node.kind() {
+ AstKind::ExportDefaultDeclaration(export_default_decl) => {
+ let ExportDefaultDeclarationKind::ObjectExpression(obj_expr) =
+ &export_default_decl.declaration
+ else {
+ return;
+ };
+ handle_object_expression(ctx, obj_expr);
+ }
+ _ => {
+ self.check_define_component(node, ctx);
+ }
+ }
+ }
+}
+
+fn collect_hash_from_object_expr(obj: &ObjectExpression) -> Option> {
+ if obj.properties.is_empty() {
+ return None;
+ }
+ let key_hash = obj
+ .properties
+ .iter()
+ .filter_map(|item| {
+ if let ObjectPropertyKind::ObjectProperty(obj_prop) = item
+ && let Some(key) = obj_prop.key.static_name()
+ {
+ Some(key.to_string())
+ } else {
+ None
+ }
+ })
+ .collect();
+ Some(key_hash)
+}
+
+fn collect_hash_from_variable_declarator(
+ ctx: &LintContext<'_>,
+ node: &AstNode,
+) -> Option> {
+ let var_decl = get_first_variable_decl_ancestor(ctx, node)?;
+ let BindingPatternKind::ObjectPattern(obj_pattern) = &var_decl.id.kind else {
+ return None;
+ };
+ let key_hash: FxHashSet = obj_pattern
+ .properties
+ .iter()
+ .filter_map(|prop| prop.key.static_name())
+ .map(|key| key.to_string())
+ .collect();
+ Some(key_hash)
+}
+
+fn get_first_variable_decl_ancestor<'a>(
+ ctx: &LintContext<'a>,
+ node: &AstNode,
+) -> Option<&'a VariableDeclarator<'a>> {
+ ctx.nodes().ancestors(node.id()).find_map(|ancestor| {
+ if let AstKind::VariableDeclarator(var_decl) = ancestor.kind() {
+ Some(var_decl)
+ } else {
+ None
+ }
+ })
+}
+
+fn process_define_props_call(
+ ctx: &LintContext,
+ first_arg_expr: &Expression,
+ key_hash: &FxHashSet,
+) {
+ let Expression::CallExpression(first_call_expr) = first_arg_expr.get_inner_expression() else {
+ return;
+ };
+ let Expression::Identifier(first_call_ident) = &first_call_expr.callee else {
+ return;
+ };
+ if first_call_ident.name != "defineProps" {
+ return;
+ }
+ let Some(type_arguments) = first_call_expr.type_arguments.as_ref() else {
+ return;
+ };
+ let Some(first_type_argument) = type_arguments.params.first() else {
+ return;
+ };
+
+ handle_type_argument(ctx, first_type_argument, key_hash);
+}
+
+fn handle_type_argument(ctx: &LintContext, ts_type: &TSType, key_hash: &FxHashSet) {
+ match ts_type {
+ // e.g. `const props = defineProps()`
+ TSType::TSTypeReference(type_ref) => {
+ let TSTypeName::IdentifierReference(ident_ref) = &type_ref.type_name else {
+ return;
+ };
+ // we need to find the reference of type_ref
+ let reference = ctx.scoping().get_reference(ident_ref.reference_id());
+ if !reference.is_type() {
+ return;
+ }
+ let reference_node = ctx.symbol_declaration(reference.symbol_id().unwrap());
+ let AstKind::TSInterfaceDeclaration(interface_decl) = reference_node.kind() else {
+ return;
+ };
+ let body = &interface_decl.body;
+ body.body.iter().for_each(|item| {
+ let (key_name, optional) = match item {
+ TSSignature::TSPropertySignature(prop_sign) => {
+ (prop_sign.key.static_name(), prop_sign.optional)
+ }
+ TSSignature::TSMethodSignature(method_sign)
+ if method_sign.kind == TSMethodSignatureKind::Method =>
+ {
+ (method_sign.key.static_name(), method_sign.optional)
+ }
+ _ => (None, false),
+ };
+ if let Some(key_name) = key_name
+ && !optional
+ && key_hash.contains(key_name.as_ref())
+ {
+ ctx.diagnostic(no_required_prop_with_default_diagnostic(
+ item.span(),
+ key_name.as_ref(),
+ ));
+ }
+ });
+ }
+ // e.g. `const props = defineProps<{ name: string }>()`
+ TSType::TSTypeLiteral(type_literal) => {
+ type_literal.members.iter().for_each(|item| {
+ let (key_name, optional) = match item {
+ TSSignature::TSPropertySignature(prop_sign) => {
+ (prop_sign.key.static_name(), prop_sign.optional)
+ }
+ TSSignature::TSMethodSignature(method_sign)
+ if method_sign.kind == TSMethodSignatureKind::Method =>
+ {
+ (method_sign.key.static_name(), method_sign.optional)
+ }
+ _ => (None, false),
+ };
+ if let Some(key_name) = key_name
+ && !optional
+ && key_hash.contains(key_name.as_ref())
+ {
+ ctx.diagnostic(no_required_prop_with_default_diagnostic(
+ item.span(),
+ key_name.as_ref(),
+ ));
+ }
+ });
+ }
+ _ => {}
+ }
+}
+
+fn handle_object_expression(ctx: &LintContext, obj: &ObjectExpression) {
+ let Some(prop) = obj.properties.iter().find(|item| {
+ if let ObjectPropertyKind::ObjectProperty(obj_prop) = item
+ && let Some(key) = obj_prop.key.static_name()
+ {
+ key == "props"
+ } else {
+ false
+ }
+ }) else {
+ return;
+ };
+ let ObjectPropertyKind::ObjectProperty(prop_obj) = prop else {
+ return;
+ };
+ let Expression::ObjectExpression(prop_obj_expr) = prop_obj.value.get_inner_expression() else {
+ return;
+ };
+ handle_prop_object(ctx, prop_obj_expr, None);
+}
+
+fn handle_prop_object(
+ ctx: &LintContext,
+ obj: &ObjectExpression,
+ key_hash: Option<&FxHashSet>,
+) {
+ obj.properties.iter().for_each(|v| {
+ if let ObjectPropertyKind::ObjectProperty(inner_prop) = v
+ && let Some(inner_key) = inner_prop.key.static_name()
+ && let Expression::ObjectExpression(inner_prop_value_expr) =
+ inner_prop.value.get_inner_expression()
+ {
+ let mut has_default_key = false;
+ let mut has_required_key = false;
+
+ // Sometimes the default value comes from the `ObjectPattern` of a `VariableDeclarator`,
+ // e.g. `const { name = 2 } = defineProps()`
+ if key_hash.is_some_and(|hash| hash.contains(inner_key.as_ref())) {
+ has_default_key = true;
+ }
+
+ for property in &inner_prop_value_expr.properties {
+ if let ObjectPropertyKind::ObjectProperty(item_obj) = property
+ && let Some(item_key) = item_obj.key.static_name()
+ {
+ if item_key == "default" {
+ has_default_key = true;
+ }
+ if item_key == "required" {
+ let Expression::BooleanLiteral(inner_value) = &item_obj.value else {
+ continue;
+ };
+ if inner_value.value {
+ has_required_key = true;
+ } else {
+ break;
+ }
+ }
+
+ if has_default_key && has_required_key {
+ ctx.diagnostic(no_required_prop_with_default_diagnostic(
+ inner_prop.span(),
+ inner_key.as_ref(),
+ ));
+ break;
+ }
+ }
+ }
+ }
+ });
+}
+
+#[test]
+fn test() {
+ use crate::tester::Tester;
+ use std::path::PathBuf;
+
+ let pass = vec![
+ (
+ r#"
+
+ "#,
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ "
+
+ ",
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ "
+
+ ",
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ "
+
+ ",
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ r#"
+
+ "#,
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ "
+
+ ",
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ ];
+
+ let fail = vec![
+ (
+ "
+ const a = defineComponent({
+ props: {
+ 'name': {
+ required: true,
+ default: 'Hello'
+ }
+ }
+ })
+ ",
+ None,
+ None,
+ Some(PathBuf::from("test.ts")),
+ ),
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ "
+
+ ",
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ "
+
+ ",
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ "
+
+ ",
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ "
+
+ ",
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ "
+
+ ",
+ None,
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ "
+
+ ",
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ), // { "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } },
+ (
+ r#"
+
+ "#,
+ Some(serde_json::json!([{ "autofix": true }])),
+ None,
+ Some(PathBuf::from("test.vue")),
+ ),
+ ];
+
+ // let _fix = vec![
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // "
+ //
+ // ",
+ // "
+ //
+ // ",
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // "
+ //
+ // ",
+ // "
+ //
+ // ",
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // "
+ //
+ // ",
+ // "
+ //
+ // ",
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // "
+ //
+ // ",
+ // "
+ //
+ // ",
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // "
+ //
+ // ",
+ // "
+ //
+ // ",
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // (
+ // r#"
+ //
+ // "#,
+ // r#"
+ //
+ // "#,
+ // Some(serde_json::json!([{ "autofix": true }])),
+ // ),
+ // ];
+
+ Tester::new(NoRequiredPropWithDefault::NAME, NoRequiredPropWithDefault::PLUGIN, pass, fail)
+ .test_and_snapshot();
+}
diff --git a/crates/oxc_linter/src/snapshots/vue_no_required_prop_with_default.snap b/crates/oxc_linter/src/snapshots/vue_no_required_prop_with_default.snap
new file mode 100644
index 0000000000000..25a598397fa86
--- /dev/null
+++ b/crates/oxc_linter/src/snapshots/vue_no_required_prop_with_default.snap
@@ -0,0 +1,197 @@
+---
+source: crates/oxc_linter/src/tester.rs
+---
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:25]
+ 3 │ props: {
+ 4 │ ╭─▶ 'name': {
+ 5 │ │ required: true,
+ 6 │ │ default: 'Hello'
+ 7 │ ╰─▶ }
+ 8 │ }
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ name: string
+ · ────────────
+ 5 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ name: string | number
+ · ─────────────────────
+ 5 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "na::me" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ 'na::me': string
+ · ────────────────
+ 5 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:5:19]
+ 4 │ interface TestPropType {
+ 5 │ name: nameType
+ · ──────────────
+ 6 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ name
+ · ────
+ 5 │ }
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ name
+ · ────
+ 5 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "na\"me2" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ 'na\\"me2'
+ · ──────────
+ 5 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "foo" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ foo(): void
+ · ───────────
+ 5 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ readonly name
+ · ─────────────
+ 5 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ readonly 'name'
+ · ───────────────
+ 5 │ age?: number
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:5:19]
+ 4 │ props: {
+ 5 │ ╭─▶ name: {
+ 6 │ │ required: true,
+ 7 │ │ default: 'Hello'
+ 8 │ ╰─▶ }
+ 9 │ }
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:5:19]
+ 4 │ props: {
+ 5 │ ╭─▶ 'name': {
+ 6 │ │ required: true,
+ 7 │ │ default: 'Hello'
+ 8 │ ╰─▶ }
+ 9 │ }
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:6:19]
+ 5 │ props: {
+ 6 │ ╭─▶ 'name': {
+ 7 │ │ required: true,
+ 8 │ │ default: 'Hello'
+ 9 │ ╰─▶ }
+ 10 │ }
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:6:19]
+ 5 │ props: {
+ 6 │ ╭─▶ name: {
+ 7 │ │ required: true,
+ 8 │ │ default: 'Hello'
+ 9 │ ╰─▶ }
+ 10 │ }
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:6:19]
+ 5 │ props: {
+ 6 │ ╭─▶ name: {
+ 7 │ │ required: true,
+ 8 │ │ default: 'Hello'
+ 9 │ ╰─▶ }
+ 10 │ }
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ const props = defineProps({
+ 4 │ ╭─▶ name: {
+ 5 │ │ required: true,
+ 6 │ │ default: 'Hello'
+ 7 │ ╰─▶ }
+ 8 │ })
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ interface TestPropType {
+ 4 │ name: string
+ · ────────────
+ 5 │ }
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ const {name="World"} = defineProps<{
+ 4 │ name: string
+ · ────────────
+ 5 │ }>();
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.
+
+ ⚠ eslint-plugin-vue(no-required-prop-with-default): Prop "name" should be optional.
+ ╭─[no_required_prop_with_default.tsx:4:19]
+ 3 │ const {name="World"} = defineProps({
+ 4 │ ╭─▶ name: {
+ 5 │ │ required: true,
+ 6 │ ╰─▶ }
+ 7 │ });
+ ╰────
+ help: Remove the `required: true` option, or drop the `required` key entirely to make this prop optional.