V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
skypyb
V2EX  ›  Vue.js

到底要怎样才能在 vue3 的 vue 文件里使用上完整的 TS 语法? 只能用一半可太离谱了

  •  
  •   skypyb · 2021-03-27 21:10:12 +08:00 · 5993 次点击
    这是一个创建于 1118 天前的主题,其中的信息可能已经有所发展或是发生改变。

    某一部分特性在 vue 文件里还能用上一点,比如类型约束,IDE 做类型推断也还行。

    但是会有些其他的特性用不了, 比如我遇到这这俩问题。
    一个是接口不能直接在 type='ts' 的 script 块中定义。(试了下类也一样)
    还有就是无法使用类型断言。

    在 .ts 文件里就能用, 在 vue 里就不能用。 你说像是接口这种就算了, 定义在 ts 文件里引进来我还能拿着用 (?其实这也很怪)。 像这种需要做类型断言的地方我就真的是没办法了...

    Unexpected reserved word 'interface' (6:4)
    
      4 |     import {NotesView} from '@/api/uinfo-service';
      5 |
    > 6 |     interface Ass {
        |     ^
      7 |         asv: string
      8 |     }
      9 |
    

     Unexpected token, expected "," (10:29)
    
       8 |         props: {
       9 |             note: {
    > 10 |                 type: Object as PropType<NotesView>,
         |                              ^
      11 |                 required: true
      12 |             }
      13 |         },
    
    
    

    搜遍了各个地方, 没找到相似的例子。我甚至不知道是什么地方出的问题。 版本?插件?配置?
    没办法只能来这求助啦


    下边贴几个文件, 大佬们帮看下呗😭


    package.json

    {
      "name": "fev-test",
      "version": "0.1.0",
      "private": true,
      "scripts": {
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build",
        "lint": "vue-cli-service lint"
      },
      "dependencies": {
        "@ant-design/icons-vue": "^6.0.1",
        "ant-design-vue": "^2.0.0",
        "axios": "^0.21.1",
        "babel-plugin-import": "^1.13.3",
        "compression-webpack-plugin": "^7.1.2",
        "core-js": "^3.8.3",
        "script-ext-html-webpack-plugin": "^2.1.5",
        "vue": "^3.0.5",
        "vue-class-component": "^8.0.0-rc.1",
        "vue-loader": "^15.9.6",
        "vue-loader-v16": "^16.0.0-beta.5.4",
        "vue-router": "^4.0.3",
        "vuex": "^4.0.0"
      },
      "devDependencies": {
        "@types/chai": "^4.2.11",
        "@types/lodash": "^4.14.161",
        "@types/mocha": "^5.2.4",
        "@typescript-eslint/eslint-plugin": "^4.15.0",
        "@typescript-eslint/parser": "^4.15.0",
        "@vue/cli-plugin-babel": "~4.5.11",
        "@vue/cli-plugin-eslint": "~4.5.11",
        "@vue/cli-plugin-router": "~4.5.11",
        "@vue/cli-plugin-typescript": "~4.5.11",
        "@vue/cli-plugin-vuex": "~4.5.11",
        "@vue/cli-service": "~4.5.11",
        "@vue/compiler-sfc": "^3.0.5",
        "@vue/eslint-config-prettier": "^6.0.0",
        "@vue/eslint-config-typescript": "^7.0.0",
        "babel-eslint": "^10.1.0",
        "eslint": "^7.20.0",
        "eslint-plugin-html": "^6.1.1",
        "eslint-plugin-prettier": "^3.3.1",
        "eslint-plugin-vue": "^7.6.0",
        "less": "^4.1.1",
        "less-loader": "^7.3.0",
        "prettier": "^2.2.1",
        "style-resources-loader": "^1.4.1",
        "typescript": "~4.1.5",
        "vue-cli-plugin-style-resources-loader": "~0.1.4",
        "webpack-bundle-analyzer": "^4.4.0"
      },
      "browserslist": [
        "> 1%",
        "last 2 versions",
        "not dead"
      ]
    }
    
    

    tsconfig.json

    {
      "compilerOptions": {
        "target": "esnext",
        "module": "esnext",
        "strict": true,
        "declaration": true,
        "jsx": "preserve",
        "importHelpers": true,
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "skipLibCheck": false,
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "suppressImplicitAnyIndexErrors": true,
        "sourceMap": false,
        "baseUrl": ".",
        "rootDir": ".",
        "types": [
          "webpack-env",
          "chai",
          "mocha"
        ],
        "paths": {
          "@/*": [
            "src/*"
          ]
        },
        "lib": [
          "esnext",
          "dom",
          "dom.iterable",
          "scripthost"
        ]
      },
      "exclude": [
        "node_modules",
        "dist"
      ]
    }
    
    

    babel.config.js

    module.exports = {
      presets: ['@vue/cli-plugin-babel/preset']
    };
    

    .eslintrc.js

    module.exports = {
        root: true,
        parserOptions: {
            parser: '@typescript-eslint/parser',
        },
        env: {
            browser: true,
            es6: true,
            node: true,
        },
        plugins: ['vue', '@typescript-eslint/eslint-plugin'],
        rules: {
            'no-unused-vars': 'off',
            '@typescript-eslint/no-explicit-any': 'off',
            '@typescript-eslint/member-delimiter-style': 'off',
            '@typescript-eslint/ban-ts-ignore': 'off',
            '@typescript-eslint/class-name-casing': 'off',
            'vue/valid-v-slot': 'off',
            'vue/experimental-script-setup-vars': 'off',
    
            'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
            'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    
            indent: ['off', 4, {SwitchCase: 1}], // 缩进不管
    
            'line-comment-position': [
                'off',
                {position: 'above', ignorePattern: 'ETC'},
            ], // 强制行注释的位置
            'vue/no-v-model-argument': 'off',
            'vue/no-setup-props-destructure': 'off',
            'vue/no-multiple-template-root': 'off',
            '@typescript-eslint/no-var-requires': 0,
            'vue/html-indent': ['warn', 4],
    
            quotes: ['error', 'single'], // 使用单引号
        },
        extends: [
            'plugin:vue/vue3-essential',
            'eslint:recommended',
            '@vue/typescript/recommended',
        ],
        overrides: [
            {
                files: [
                    '**/tests/*.{j,t}s?(x)',
                    '**/tests/**/*.spec.{j,t}s?(x)',
                    '**/tests/*.spec.{j,t}s?(x)',
                ],
                env: {
                    mocha: true,
                },
            },
        ],
    };
    
    

    vue.config.js

    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
        .BundleAnalyzerPlugin
    
    const IS_DEV = process.env.NODE_ENV !== 'production'
    /**
     * @todo 开发环境配置
     * 某些实用工具,plugins 和 loaders 都只能在构建生产环境时才有用
     * 在开发时使用 UglifyJsPlugin 来压缩和修改代码是没有意义的,不压缩
     */
    
    const DEVELOPMENT = webpackConfig => {
        /**
         * @todo 启用 eval-source-map 更好的测试
         * 每个模块使用 eval() 执行,并且 source map 转换为 DataUrl 后添加到 eval() 中。
         * 初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。
         * 行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map 。
         */
    
        webpackConfig.store.set('devtool', 'eval-source-map')
        webpackConfig.plugin('html').tap(([options]) => [
            Object.assign(options, {
                minify: false,
                chunksSortMode: 'none'
            })
        ])
    
        // webpackConfig.plugin('BundleAnalyzerPlugin').use(BundleAnalyzerPlugin)
    
        return webpackConfig
    }
    
    /**
     * @todo 生产环境配置
     * 每个额外的 loader/plugin 都有启动时间。尽量少使用不同的工具
     */
    
    const PRODUCTION = webpackConfig => {
        /**
         * @todo 不需要启用 source-map,去除 console 的情况下 source-map 根本没用,还浪费大量时间和空间
         * 详情见: https://webpack.js.org/configuration/devtool/#devtool
         */
        webpackConfig.store.set('devtool', '')
        webpackConfig.plugin('html').tap(([options]) => [
            Object.assign(options, {
                minify: {
                    removeComments: true,
                    removeCommentsFromCDATA: true,
                    collapseWhitespace: true,
                    conservativeCollapse: false,
                    collapseInlineTagWhitespace: true,
                    collapseBooleanAttributes: true,
                    removeRedundantAttributes: true,
                    removeAttributeQuotes: false,
                    removeEmptyAttributes: true,
                    removeScriptTypeAttributes: true,
                    removeStyleLinkTypeAttributes: true,
                    useShortDoctype: true,
                    minifyJS: true,
                    minifyCSS: true
                },
                cache: true, // 仅在文件被更改时发出文件
                hash: true, // true 则将唯一的 webpack 编译哈希值附加到所有包含的脚本和 CSS 文件中,这对于清除缓存很有用
                scriptLoading: 'defer', // 现代浏览器支持非阻塞 javascript 加载('defer'),以提高页面启动性能。
                inject: true, // true 所有 javascript 资源都将放置在 body 元素的底部
                chunksSortMode: 'none'
            })
        ])
    
        return webpackConfig
    }
    
    module.exports = {
        publicPath: '/',
        css: {
            loaderOptions: {
                less: {
                    sourceMap: IS_DEV,
                    lessOptions: {
                        javascriptEnabled: true
                    }
                }
            }
        },
        devServer: {
            proxy: 'http://10.10.10.115:8002'
        },
        pluginOptions: {
            /** 全局加载 less 的 webpack 插件  */
            'style-resources-loader': {
                preProcessor: 'less',
                patterns: ['./src/styles/index.less']
            }
        },
        /**
         * @description 去掉 console 信息
         *  config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true;
         *  html-webpack-plugin 插件配置详情见 https://github.com/jantimon/html-webpack-plugin#options
         */
        configureWebpack: config => {
            config.optimization = {
                splitChunks: {
                    chunks: 'all',
                    minSize: 3000, // (默认值:30000 )块的最小大小。
                    minChunks: 1, //(默认值:1 )在拆分之前共享模块的最小块数
                    maxAsyncRequests: 5, //(默认值为 5 )按需加载时并行请求的最大数量
                    maxInitialRequests: 6, // (默认值为 3 )入口点的最大并行请求数
                    automaticNameDelimiter: '-',
                    name: true,
                    cacheGroups: {
                        lodash: {
                            name: 'lodash',
                            test: /[\\/]node_modules[\\/]lodash[\\/]/,
                            priority: 20
                        },
                        vue: {
                            name: 'vue',
                            test: /[\\/]node_modules[\\/]vue[\\/]/
                        },
                        vuex: {
                            name: 'vuex',
                            test: /[\\/]node_modules[\\/]vuex[\\/]/
                        },
                        'vuex-presistedstate': {
                            name: 'vuex-presistedstate',
                            test: /[\\/]node_modules[\\/]vuex-presistedstate[\\/]/
                        },
                        'vue-router': {
                            name: 'vue-router',
                            test: /[\\/]node_modules[\\/]vue-router[\\/]/
                        },
                        'ant-design-vue': {
                            name: 'ant-design-vue',
                            test: /[\\/]node_modules[\\/]ant-design-vue[\\/]/
                        },
                        moment: {
                            name: 'moment',
                            test: /[\\/]node_modules[\\/]moment[\\/]/,
                            priority: 40
                        }
                    }
                }
            }
        },
        chainWebpack: config => {
            config.resolve.symlinks(true)
    
            if (process.env.use_analyzer) {
                config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin)
            }
    
            IS_DEV ? DEVELOPMENT(config) : PRODUCTION(config)
        },
        productionSourceMap: false,
        lintOnSave: true
    }
    
    
    24 条回复    2021-03-29 16:26:32 +08:00
    iold
        1
    iold  
       2021-03-27 21:52:12 +08:00
    你应该需要告诉 ts, *.vue 也是 ts 文件, ts 配置里 include 一下 ts 存在哪些文件后缀文件里. 例如 "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
    skypyb
        2
    skypyb  
    OP
       2021-03-27 22:00:59 +08:00
    @iold 刚按照你说的试了下, 仍然不行。 其实是可以在 vue 里用 ts,也配置过了 shims-vue.d.ts ,只是有不少莫名其妙的问题 (比如我上边说的)。
    cereschen
        3
    cereschen  
       2021-03-27 22:45:45 +08:00
    你这个报错是哪个服务报错的 发完整一点
    shakukansp
        4
    shakukansp  
       2021-03-28 00:31:37 +08:00
    lang="ts"
    EPr2hh6LADQWqRVH
        5
    EPr2hh6LADQWqRVH  
       2021-03-28 00:36:43 +08:00
    Vue 和 TypeScript 不兼容的, 只能是主要逻辑单独写在外面的 ts 文件里,vue 只做引用调用。
    想要 vue 里面使用 ts, 太难受了,yyx 不支持。
    einq7
        6
    einq7  
       2021-03-28 00:40:09 +08:00 via iPhone
    @avastms 请问 yyx 是什么
    ShareDuck
        7
    ShareDuck  
       2021-03-28 01:27:46 +08:00 via Android
    @einq7 是指作者吧。
    ruoxie
        8
    ruoxie  
       2021-03-28 02:01:06 +08:00
    直接 tsx 不香吗?写 template 没提示,还不如不用 TS

    https://github.com/lowcoding/lowcode-vscode/blob/vite2/webview-vue-webpack/src/views/snippet/Detail/index.tsx

    写一个 vscode 插件 webview 部分的时候分别用 react 跟 vue3 撸了一次,简直不要太像
    Rocketer
        9
    Rocketer  
       2021-03-28 04:50:52 +08:00 via iPhone   ❤️ 1
    个人感觉因为 Vue 定位于简单,所以就用最简单的 JS,这是匹配的。

    想用 TS,就最好整体升级,用 React 或者 Angular 。非得用 TS 写 Vue,有种用专业咖啡机做速溶咖啡的感觉,没必要,也没什么好的效果。
    ericls
        10
    ericls  
       2021-03-28 04:52:53 +08:00 via iPhone   ❤️ 1
    这就是我所说的 用做框架的思想做语言 肯定会出问题
    Zzzz77
        11
    Zzzz77  
       2021-03-28 07:14:40 +08:00 via Android   ❤️ 3
    首先 vue3 绝对绝对绝对是支持 ts 的,上面说不兼容的几位朋友应该是对 vue 并不太了解。
    回到楼主的问题,第一点,script 标签应该是<script lang="ts">,而不是 type="ts",我不确定是不是你的笔误。
    如果排除这一点,其实以上信息很难看出问题出在哪,我只能给几个建议:
    一,如果方便的话,可以贴出仓库地址,大家可以帮你检查问题出在哪
    二,去看看一些 vue3 + ts 的案例,看看区别在哪里,这种案例 github 一搜一堆,我前段时间也在 v 站上发过一个 vue3 + ts 的项目,你也可以翻我的主题看看
    三,最简单有效的办法,直接用 cli 生成一个 vue3 + ts 的项目模板,对比检查问题出在什么地方

    顺带一提,上面有朋友认为写 template 没提示,还不如不用 TS,个人不太认同,template 是可以通过 ide 插件(如 vscode )支持 ts 提示的,至于 template 和 tsx 哪个好,就见仁见智了~
    iold
        12
    iold  
       2021-03-28 08:30:52 +08:00 via iPhone
    @skypyb #2 你用 cli 生成个 vue3+ts 的项目做对比。
    bojackhorseman
        13
    bojackhorseman  
       2021-03-28 10:09:11 +08:00 via iPhone
    vue2 + composition-api + ts 写起来也蛮香的
    solos
        14
    solos  
       2021-03-28 10:56:15 +08:00
    建议换 angular🐶
    tolking
        15
    tolking  
       2021-03-28 11:55:18 +08:00
    vscode + vetur 没有遇到这些问题,模版的提示据说要等到 setup 稳定后增加。毕竟不是标准的 ts,需要通过 ide 插件辅助支持。
    Cbdy
        16
    Cbdy  
       2021-03-28 12:07:39 +08:00 via Android
    应该不行🙅
    beginor
        17
    beginor  
       2021-03-28 12:40:20 +08:00 via Android
    要体验完整的 ts 支持, 建议试试 angular,ng 11 默认开启严格模式, 真是强迫症的福音!

    另外 react 也不错, tsx 有特供的 ts 支持!

    为啥非得在 vue 上吊死?
    suzic
        18
    suzic  
       2021-03-28 12:50:38 +08:00 via Android
    我们就是那样用的,没碰到楼主说的问题。建议楼主检查下是不是 type='ts' 的原因,正确的写法是"lang=ts"
    emilll
        19
    emilll  
       2021-03-28 18:56:23 +08:00
    楼主写法没错的

    <script lang="ts"></script>

    另外 vue3 推荐使用 volar 插件
    hotsymbol
        20
    hotsymbol  
       2021-03-28 22:11:01 +08:00
    用 Typescript 写 Vue ?是脑子不好使吗?用 React 和 Angular 不能写 Typescript 吗?还是不思进取吗?
    Jaosn
        21
    Jaosn  
       2021-03-29 00:07:22 +08:00   ❤️ 1
    这个帖子给我看乐了,几个回复也十分有趣,比如我楼上这位
    Sparetire
        22
    Sparetire  
       2021-03-29 02:54:13 +08:00 via Android
    建议 Vue3 用 volar, Vue2 用 vetur
    rodrick
        23
    rodrick  
       2021-03-29 11:43:03 +08:00
    不明白 vue 写 ts 怎么就有人觉得能上升到脑子不好了
    xiaoliaoliao
        24
    xiaoliaoliao  
       2021-03-29 16:26:32 +08:00
    推荐 volar 但是这插件也不是很成熟 eslint 没法用,如果需要未使用变量报错可以在 tsconfig 的 compilerOptions 中增加字段"noUnusedLocals": true
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4037 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 05:15 · PVG 13:15 · LAX 22:15 · JFK 01:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.