Vue 3迁移时Props类型为unknown触发TS2322报错问题
Vue 3 迁移构建Props类型识别为unknown问题
问题现象
参考Vue 3迁移构建指南将应用从Vue 2升级至Vue 3过程中,遇到Props类型识别异常:Linter判定所有Props类型为unknown,但模板本身未提示任何错误。
复现代码
- Prop定义:
cancelText: { type: String as PropType<string>, default: "", },
- 计算属性中调用Prop:
cancelButtonText(): string { return this.cancelText || this.$t("PRODUCT.ACTION_BAR.BACK"); },
异常表现
- 编辑器内悬浮到对应变量上时可正确显示类型,编辑器本身类型识别逻辑正常:

- 启动应用服务时,终端抛出如下TS错误:
TS2322: Type 'unknown' is not assignable to type 'string'.
报错截图:
当前环境配置
初步判断问题为依赖版本不兼容,或Vue 3对应Lint规则未正确配置,当前项目配置如下:
依赖版本
"dependencies": { "@fortawesome/fontawesome-pro": "^5.15.3", "@fortawesome/fontawesome-svg-core": "^1.2.35", "@fortawesome/free-brands-svg-icons": "5.14.0", "@fortawesome/pro-regular-svg-icons": "^5.15.3", "@fortawesome/vue-fontawesome": "^2.0.2", "@sentry/browser": "^6.6.0", "@sentry/integrations": "^6.6.0", "@sentry/tracing": "^6.6.0", "@vue/compat": "3.2.36", "ant-design-vue": "^2.2.8", "axios": "^0.21.1", "jest": "^26.6.3", "normalize.css": "^8.0.1", "register-service-worker": "^1.7.1", "uuid": "^8.3.2", "vue": "^3.2.36", "vue-cookies": "^1.8.1", "vue-flag-icon": "^1.0.6", "vue-i18n": "^9.1.10", "vue-router": "^4.0.15", "vue3-touch-events": "^4.1.0", "vuex": "^4.0.2" }, "devDependencies": { "@playwright/test": "^1.15.2", "@vue/cli-plugin-pwa": "^4.5.13", "@vue/cli-plugin-typescript": "^5.0.4", "@vue/cli-service": "^4.5.13", "@vue/compiler-sfc": "^3.2.36", "eslint": "^8.16.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-playwright": "^0.9.0", "eslint-plugin-vue": "^9.1.0", "html-loader": "^1.3.2", "jest": "^26.6.3", "less": "^3.13.0", "less-loader": "^7.3.0", "node-sass": "^5.0.0", "sass-loader": "^10.2.0", "typescript": "~4.7.2" }
ESLint配置
module.exports = { env: { browser: true, es2020: true, node: true, }, extends: [ "plugin:playwright/playwright-test", "plugin:vue/base", "plugin:vue/essential", "plugin:vue/strongly-recommended", "eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier", ], parser: "vue-eslint-parser", parserOptions: { sourceType: "module", parser: { js: false, ts: "@typescript-eslint/parser", "<template>": false, }, }, plugins: ["vue", "@typescript-eslint"], rules: { "no-else-return": "error", "no-var": "error", "prefer-const": "error", "prefer-arrow-callback": "error", "no-console": "error", "no-unused-vars": "off", "@typescript-eslint/no-unnecessary-type-assertion": "warn", "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/no-unused-vars": [ "warn", { vars: "all", args: "after-used", ignoreRestSiblings: false, argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_", }, ], "vue/no-unused-properties": [ "warn", { groups: ["props", "data", "computed", "methods", "setup"], deepData: true, ignorePublicMembers: true, }, ], "vue/html-self-closing": [ "warn", { html: { void: "always", }, svg: "always", math: "always", }, ], "vue/multiline-html-element-content-newline": "off", "vue/html-closing-bracket-newline": "off", "vue/singleline-html-element-content-newline": "off", "vue/html-indent": "off", "vue/max-attributes-per-line": "off", "vue/this-in-template": ["error", "never"], "vue/attributes-order": [ "error", { order: [ "DEFINITION", "LIST_RENDERING", "CONDITIONALS", "RENDER_MODIFIERS", "GLOBAL", "UNIQUE", "TWO_WAY_BINDING", "OTHER_DIRECTIVES", "OTHER_ATTR", "EVENTS", "CONTENT", ], alphabetical: false, }, ], "vue/order-in-components": [ "error", { order: [ "el", "name", "parent", "functional", ["delimiters", "comments"], ["components", "directives", "filters"], "extends", "mixins", "inheritAttrs", "model", ["props", "propsData"], "data", "computed", "watch", "LIFECYCLE_HOOKS", "methods", ["template", "render"], "renderError", ], }, ], }, overrides: [ { files: ["**/*.ts"], parser: "@typescript-eslint/parser", plugins: ["@typescript-eslint"], parserOptions: { tsconfigRootDir: __dirname, project: "tsconfig.json", }, extends: [ "plugin:@typescript-eslint/recommended", ], rules: { "@typescript-eslint/no-explicit-any": ["warn", { ignoreRestArgs: false }], }, }, { files: ["**/tests/*.{j,t}s?(x)", "**/tests/unit/**/*.spec.{j,t}s?(x)"], env: { jest: true, }, }, ], };
问题根因
该异常由两个配置问题共同导致:
- 使用
@vue/compat迁移构建时,未在TS配置中做类型别名映射,TS默认读取原生Vue3类型定义,和兼容构建的运行时逻辑不匹配 - ESLint解析规则仅对纯
.ts文件开启了TS类型检查,.vue文件内的脚本块未关联tsconfig配置,导致Props类型无法被正确推导,默认回退为unknown
修复步骤
- 修改项目根目录
tsconfig.json,添加路径别名映射,将vue的类型指向@vue/compat,同时确保.vue文件被纳入类型检查范围:
{ "compilerOptions": { "target": "esnext", "module": "esnext", "moduleResolution": "node", "strict": true, "paths": { "vue": ["@vue/compat"] } }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] }
- 调整ESLint配置,删除overrides中针对
.ts文件单独指定parser的冗余配置,全局配置TS解析器关联tsconfig,让.vue文件内的TS代码也能获得正确类型推导:
// 全局parserOptions修改为 parserOptions: { sourceType: "module", parser: "@typescript-eslint/parser", tsconfigRootDir: __dirname, project: "./tsconfig.json", extraFileExtensions: [".vue"] }, // 删除overrides中针对ts文件单独指定parser的冗余配置块
- 所有组件选项必须用
defineComponent包裹,这是Vue3下TS正确推导组件内this类型的必要条件,示例代码:
import { defineComponent, PropType } from 'vue' export default defineComponent({ props: { cancelText: { type: String as PropType<string>, default: "", }, }, computed: { cancelButtonText(): string { return this.cancelText || this.$t("PRODUCT.ACTION_BAR.BACK"); } } })
- 删除
node_modules目录和包管理器锁文件,重新安装依赖,重启IDE的TS服务和本地开发服务,报错即可消除。
内容的提问来源于stack exchange,提问作者Jonas Tamoševičius
相关产品推荐
相关产品推荐

