diff --git a/docs/rules/README.md b/docs/rules/README.md
index 6e879b2f6..4a46ba66c 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -352,6 +352,7 @@ For example:
| [vue/no-v-text](./no-v-text.md) | disallow use of v-text | |
| [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: |
| [vue/prefer-import-from-vue](./prefer-import-from-vue.md) | enforce import from 'vue' instead of import from '@vue/*' | :wrench: |
+| [vue/prefer-prop-type-boolean-first](./prefer-prop-type-boolean-first.md) | enforce `Boolean` comes first in component prop types | :bulb: |
| [vue/prefer-separate-static-class](./prefer-separate-static-class.md) | require static class names in template to be in a separate `class` attribute | :wrench: |
| [vue/prefer-true-attribute-shorthand](./prefer-true-attribute-shorthand.md) | require shorthand form attribute when `v-bind` value is `true` | :bulb: |
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | |
diff --git a/docs/rules/prefer-prop-type-boolean-first.md b/docs/rules/prefer-prop-type-boolean-first.md
new file mode 100644
index 000000000..3bf2edb4a
--- /dev/null
+++ b/docs/rules/prefer-prop-type-boolean-first.md
@@ -0,0 +1,60 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/prefer-prop-type-boolean-first
+description: enforce `Boolean` comes first in component prop types
+---
+# vue/prefer-prop-type-boolean-first
+
+> enforce `Boolean` comes first in component prop types
+
+- :exclamation: ***This rule has not been released yet.***
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+When declaring types of a property in component, we can use array style to accept multiple types.
+
+When using components in template,
+we can use shorthand-style property if its value is `true`.
+
+However, if a property allows `Boolean` or `String` and we use it with shorthand form in somewhere else,
+different types order can introduce different behaviors:
+If `Boolean` comes first, it will be `true`; if `String` comes first, it will be `""` (empty string).
+
+See [this demo](https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCBNeUNvbXBvbmVudCBmcm9tICcuL015Q29tcG9uZW50LnZ1ZSdcbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIFNob3J0aGFuZCBmb3JtOlxuICA8TXlDb21wb25lbnQgYm9vbCBib29sLW9yLXN0cmluZyBzdHJpbmctb3ItYm9vbCAvPlxuICBcbiAgTG9uZ2hhbmQgZm9ybTpcbiAgPE15Q29tcG9uZW50IDpib29sPVwidHJ1ZVwiIDpib29sLW9yLXN0cmluZz1cInRydWVcIiA6c3RyaW5nLW9yLWJvb2w9XCJ0cnVlXCIgLz5cbjwvdGVtcGxhdGU+IiwiaW1wb3J0LW1hcC5qc29uIjoie1xuICBcImltcG9ydHNcIjoge1xuICAgIFwidnVlXCI6IFwiaHR0cHM6Ly9zZmMudnVlanMub3JnL3Z1ZS5ydW50aW1lLmVzbS1icm93c2VyLmpzXCJcbiAgfVxufSIsIk15Q29tcG9uZW50LnZ1ZSI6IjxzY3JpcHQ+XG5leHBvcnQgZGVmYXVsdCB7XG4gIHByb3BzOiB7XG4gICAgYm9vbDogQm9vbGVhbixcbiAgICBib29sT3JTdHJpbmc6IFtCb29sZWFuLCBTdHJpbmddLFxuICAgIHN0cmluZ09yQm9vbDogW1N0cmluZywgQm9vbGVhbl0sXG4gIH1cbn1cbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIDxwcmU+XG5ib29sOiB7e2Jvb2x9fSAoe3sgdHlwZW9mIGJvb2wgfX0pXG5ib29sT3JTdHJpbmc6IHt7Ym9vbE9yU3RyaW5nfX0gKHt7IHR5cGVvZiBib29sT3JTdHJpbmcgfX0pXG5zdHJpbmdPckJvb2w6IHt7c3RyaW5nT3JCb29sfX0gKHt7IHR5cGVvZiBzdHJpbmdPckJvb2wgfX0pXG4gIDwvcHJlPlxuPC90ZW1wbGF0ZT4ifQ==).
+
+
+
+```vue
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/prefer-true-attribute-shorthand](./prefer-true-attribute-shorthand.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-prop-type-boolean-first.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-prop-type-boolean-first.js)
diff --git a/docs/rules/prefer-true-attribute-shorthand.md b/docs/rules/prefer-true-attribute-shorthand.md
index f2704f4cd..c64f9a8c5 100644
--- a/docs/rules/prefer-true-attribute-shorthand.md
+++ b/docs/rules/prefer-true-attribute-shorthand.md
@@ -107,6 +107,7 @@ Default options is `"always"`.
## :couple: Related Rules
- [vue/no-boolean-default](./no-boolean-default.md)
+- [vue/prefer-prop-type-boolean-first](./prefer-prop-type-boolean-first.md)
## :rocket: Version
diff --git a/lib/index.js b/lib/index.js
index 97ff805d3..4e29c8ed0 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -158,6 +158,7 @@ module.exports = {
'order-in-components': require('./rules/order-in-components'),
'padding-line-between-blocks': require('./rules/padding-line-between-blocks'),
'prefer-import-from-vue': require('./rules/prefer-import-from-vue'),
+ 'prefer-prop-type-boolean-first': require('./rules/prefer-prop-type-boolean-first'),
'prefer-separate-static-class': require('./rules/prefer-separate-static-class'),
'prefer-template': require('./rules/prefer-template'),
'prefer-true-attribute-shorthand': require('./rules/prefer-true-attribute-shorthand'),
diff --git a/lib/rules/prefer-prop-type-boolean-first.js b/lib/rules/prefer-prop-type-boolean-first.js
new file mode 100644
index 000000000..215262d3b
--- /dev/null
+++ b/lib/rules/prefer-prop-type-boolean-first.js
@@ -0,0 +1,115 @@
+/**
+ * @author Pig Fang
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * @param {ArrayExpression} node
+ * @param {RuleContext} context
+ */
+function checkArrayExpression(node, context) {
+ const booleanType = node.elements.find(
+ (element) =>
+ element && element.type === 'Identifier' && element.name === 'Boolean'
+ )
+ if (!booleanType) {
+ return
+ }
+ const booleanTypeIndex = node.elements.indexOf(booleanType)
+ if (booleanTypeIndex > 0) {
+ context.report({
+ node: booleanType,
+ messageId: 'shouldBeFirst',
+ suggest: [
+ {
+ messageId: 'moveToFirst',
+ fix: (fixer) => {
+ const sourceCode = context.getSourceCode()
+
+ const elements = node.elements.slice()
+ elements.splice(booleanTypeIndex, 1)
+ const code = elements
+ .filter(utils.isDef)
+ .map((element) => sourceCode.getText(element))
+ code.unshift('Boolean')
+
+ return fixer.replaceText(node, `[${code.join(', ')}]`)
+ }
+ }
+ ]
+ })
+ }
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'enforce `Boolean` comes first in component prop types',
+ categories: undefined,
+ url: 'https://eslint.vuejs.org/rules/prefer-prop-type-boolean-first.html'
+ },
+ fixable: null,
+ // eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- `context.report` with suggestion is not recognized in `checkArrayExpression`
+ hasSuggestions: true,
+ schema: [],
+ messages: {
+ shouldBeFirst: 'Type `Boolean` should be at first in prop types.',
+ moveToFirst: 'Move `Boolean` to be first in prop types.'
+ }
+ },
+ /** @param {RuleContext} context */
+ create(context) {
+ /**
+ * @param {import('../utils').ComponentProp} prop
+ */
+ function checkProperty(prop) {
+ const { value } = prop
+ if (!value) {
+ return
+ }
+
+ if (value.type === 'ArrayExpression') {
+ checkArrayExpression(value, context)
+ } else if (value.type === 'ObjectExpression') {
+ const type = value.properties.find(
+ /** @return {property is Property} */
+ (property) =>
+ property.type === 'Property' &&
+ utils.getStaticPropertyName(property) === 'type'
+ )
+ if (!type || type.value.type !== 'ArrayExpression') {
+ return
+ }
+ checkArrayExpression(type.value, context)
+ }
+ }
+
+ return utils.compositingVisitors(
+ utils.defineScriptSetupVisitor(context, {
+ onDefinePropsEnter(_, props) {
+ props.forEach(checkProperty)
+ }
+ }),
+ utils.executeOnVue(context, (obj) => {
+ const props = utils.getComponentPropsFromOptions(obj)
+ props.forEach(checkProperty)
+ })
+ )
+ }
+}
diff --git a/tests/lib/rules/prefer-prop-type-boolean-first.js b/tests/lib/rules/prefer-prop-type-boolean-first.js
new file mode 100644
index 000000000..9f67e336b
--- /dev/null
+++ b/tests/lib/rules/prefer-prop-type-boolean-first.js
@@ -0,0 +1,317 @@
+/**
+ * @author Pig Fang
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/prefer-prop-type-boolean-first')
+
+const tester = new RuleTester({
+ parser: require.resolve('vue-eslint-parser'),
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: 'module'
+ }
+})
+
+tester.run('prefer-prop-type-boolean-first', rule, {
+ valid: [
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ }
+ ],
+ invalid: [
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ messageId: 'shouldBeFirst',
+ line: 5,
+ column: 29,
+ suggestions: [
+ {
+ messageId: 'moveToFirst',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ messageId: 'shouldBeFirst',
+ line: 5,
+ column: 37,
+ suggestions: [
+ {
+ messageId: 'moveToFirst',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ messageId: 'shouldBeFirst',
+ line: 5,
+ column: 29,
+ suggestions: [
+ {
+ messageId: 'moveToFirst',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ messageId: 'shouldBeFirst',
+ line: 5,
+ column: 37,
+ suggestions: [
+ {
+ messageId: 'moveToFirst',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ messageId: 'shouldBeFirst',
+ line: 4,
+ column: 27,
+ suggestions: [
+ {
+ messageId: 'moveToFirst',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ messageId: 'shouldBeFirst',
+ line: 4,
+ column: 35,
+ suggestions: [
+ {
+ messageId: 'moveToFirst',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ messageId: 'shouldBeFirst',
+ line: 4,
+ column: 35,
+ suggestions: [
+ {
+ messageId: 'moveToFirst',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ messageId: 'shouldBeFirst',
+ line: 4,
+ column: 43,
+ suggestions: [
+ {
+ messageId: 'moveToFirst',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ }
+ ]
+})