How to add Typescript to the generated skeleton app?

Hi, im trying to add Typescript to the generated skeleton app, but running into a couple errors when using Interface. Im new to using Typescript so im sure i’ve missed something crucial, a version missmatch or something. The linter seems to work, because it says I cant assign a string to a number for example. I think there is a config in nextclouds babel-loader that might be the problem?

The first error I get is when I try running this code I found on their official documentation:

<script setup lang="ts">
interface TestProps{
	foo: string
	bar: number
}
const props = defineProps<TestProps>()
</script>

And the error I get is:

ERROR in ./src/components/TestComp.vue?vue&type=script&setup=true&lang=ts (./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/components/TestComp.vue?vue&type=script&setup=true&lang=ts)
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: /home/myuser/sdkmc/src/components/TestComp.vue: Unexpected reserved word 'interface'. (2:0)

  1 | import { defineComponent as _defineComponent } from 'vue'
> 2 | interface NotProps123 {
    | ^
  3 |   foo: string
  4 |   bar: number
  5 | }
    at constructor (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:353:19)
    at JSXParserMixin.raise (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:3277:19)
    at JSXParserMixin.checkReservedWord (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:11737:12)
    at JSXParserMixin.parseIdentifierName (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:11717:12)
    at JSXParserMixin.parseIdentifier (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:11692:23)
    at JSXParserMixin.parseExprAtom (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10938:27)
    at JSXParserMixin.parseExprAtom (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:6821:20)
    at JSXParserMixin.parseExprSubscripts (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10580:23)
    at JSXParserMixin.parseUpdate (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10563:21)
    at JSXParserMixin.parseMaybeUnary (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10541:23)
    at JSXParserMixin.parseMaybeUnaryOrPrivate (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10395:61)
    at JSXParserMixin.parseExprOps (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10400:23)
    at JSXParserMixin.parseMaybeConditional (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10377:23)
    at JSXParserMixin.parseMaybeAssign (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10338:21)
    at JSXParserMixin.parseExpressionBase (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10292:23)
    at /home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10288:39
    at JSXParserMixin.allowInAnd (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:11921:16)
    at JSXParserMixin.parseExpression (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:10288:17)
    at JSXParserMixin.parseStatementContent (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:12362:23)
    at JSXParserMixin.parseStatementLike (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:12229:17)
    at JSXParserMixin.parseModuleItem (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:12206:17)
    at JSXParserMixin.parseBlockOrModuleBlockBody (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:12786:36)
    at JSXParserMixin.parseBlockBody (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:12779:10)
    at JSXParserMixin.parseProgram (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:12106:10)
    at JSXParserMixin.parseTopLevel (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:12096:25)
    at JSXParserMixin.parse (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:13895:10)
    at parse (/home/myuser/sdkmc/node_modules/@babel/parser/lib/index.js:13916:26)
    at parser (/home/myuser/sdkmc/node_modules/@babel/core/lib/parser/index.js:41:34)
    at parser.next (<anonymous>)
    at normalizeFile (/home/myuser/sdkmc/node_modules/@babel/core/lib/transformation/normalize-file.js:64:37)
    at normalizeFile.next (<anonymous>)
    at run (/home/myuser/sdkmc/node_modules/@babel/core/lib/transformation/index.js:21:50)
    at run.next (<anonymous>)
    at transform (/home/myuser/sdkmc/node_modules/@babel/core/lib/transform.js:22:33)
    at transform.next (<anonymous>)
    at step (/home/myuser/sdkmc/node_modules/gensync/index.js:261:32)
    at /home/myuser/sdkmc/node_modules/gensync/index.js:273:13
    at async.call.result.err.err (/home/myuser/sdkmc/node_modules/gensync/index.js:223:11)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at runNextTicks (node:internal/process/task_queues:64:3)
    at process.processImmediate (node:internal/timers:449:9)
 @ ./src/components/TestComp.vue?vue&type=script&setup=true&lang=ts 1:0-178 1:194-197 1:199-374 1:199-374
 @ ./src/components/TestComp.vue 2:0-70 3:0-65 3:0-65 9:2-8
 @ ./src/main.js 9:0-49 22:17-25

webpack 5.90.1 compiled with 1 error in 2217 ms


What i’ve added to the skeleton app is:

webpack.config.js (the commented out text i’ve also with a separate file that have the const config= in it, tried but ran into the same problem):

const webpackConfig = require('@nextcloud/webpack-vue-config')

// webpackConfig.entry['typescript-webpack-config'] = path.join(__dirname, 'typescript_webpack_config.js')
// webpackConfig.plugins.push(new webpack.IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ }))
module.exports = webpackConfig

// eslint-disable-next-line
const config = {
	resolve: {
		extensions: ['.ts', '.js'],
	},
	module: {
		rules: [
			{
				test: /\.ts$/,
				loader: 'ts-loader',
				options: {
					appendTsSuffixTo: [/\.vue$/],
					transpileOnly: true,
				},
			},
			{
				test: /\.vue$/,
				loader: 'vue-loader',
			},
		],
	},
}

package.json dependencies:

  "dependencies": {
    "@nextcloud/axios": "^1.10.0",
    "@nextcloud/dialogs": "^3.1.4",
    "@nextcloud/router": "^2.0.0",
    "@nextcloud/vue": "^5.4.0",
    "vue": "^2.7.0",
    "vue-loader": "^15.11.1",
    "vue-template-compiler": "^2.7.16",
    "webpack": "^5.90.1"
  },
  "browserslist": [
    "extends @nextcloud/browserslist-config"
  ],
  "engines": {
    "node": "^16.0.0",
    "npm": "^7.0.0 || ^8.0.0"
  },
  "devDependencies": {
    "@babel/plugin-syntax-flow": "^7.23.3",
    "@babel/preset-typescript": "^7.23.3",
    "@nextcloud/babel-config": "^1.0.0",
    "@nextcloud/browserslist-config": "^2.2.0",
    "@nextcloud/eslint-config": "^8.3.0",
    "@nextcloud/stylelint-config": "^2.1.2",
    "@nextcloud/webpack-vue-config": "^5.2.1",
    "@vue/cli-plugin-babel": "^5.0.8",
    "@vue/tsconfig": "^0.5.1",
    "ts-loader": "^9.5.1",
    "typescript": "^5.3.3"
  }

tsconfig.json:

{
  "compilerOptions": {
    "extends": "@vue/tsconfig/tsconfig.json",
    "allowJs": true,
    "baseUrl": ".",
    "jsx": "preserve",
    "module": "es2015",
    "moduleResolution": "node",
    "paths": {
      "@/*": ["src/*"]
    },
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "target": "es5"
  }
}

.eslintrc.js:

module.exports = {
	extends: [
		'@nextcloud/eslint-config/typescript',
	]
}

Well Typescript is basically “just” a framework over the JavaScript. Nextcloud uses another framework already. Or several actually.

For security reasons some things are prevented. One of those is to extend the core interface. However this is a guess based on my assumption due to my conception of the Security model in Nextcloud. It would make good sense in a security perspective in general.

Honestly, I am no typescript developer. The co-maintainer of the cookbook recently migrated a non-typescript project to a typescript project. I know this is quite some changes but you might be able to browse the individual commits. Maybe you can take this as a basis for your needs.

1 Like

Thanks, i’ll check this out!

1 Like