I have a laravel, vue3 and typescript project.
in the users module, UsersGrid, I have a custom composable, useUser , I want to type it, but every way I try, an error always comes up
as a custom hook ( composable ) i cant npm install @types/XYZ
tsconfig in root :
{
"compilerOptions": {
// As long as you are using a build tool, we recommend you to author and ship in ES modules.
// Even if you are targeting Node.js, because
// - `CommonJS` is too outdated
// - the ecosystem hasn't fully caught up with `Node16`/`NodeNext`
// This recommendation includes environments like Vitest, Vite Config File, Vite SSR, etc.
"module": "ESNext",
// We expect users to use bundlers.
// So here we enable some resolution features that are only available in bundlers.
"moduleResolution": "bundler",
"resolveJsonModule": true,
// `allowImportingTsExtensions` can only be used when `noEmit` or `emitDeclarationOnly` is set.
// But `noEmit` may cause problems with solution-style tsconfigs:
// <https://github.com/microsoft/TypeScript/issues/49844>
// And `emitDeclarationOnly` is not always wanted.
// Considering it's not likely to be commonly used in Vue codebases, we don't enable it here.
// Required in Vue projects
"jsx": "preserve",
"jsxImportSource": "vue",
// `"noImplicitThis": true` is part of `strict`
// Added again here in case some users decide to disable `strict`.
// This enables stricter inference for data properties on `this`.
"noImplicitThis": true,
"strict": true,
// <https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#verbatimmodulesyntax>
// Any imports or exports without a type modifier are left around. This is important for `<script setup>`.
// Anything that uses the type modifier is dropped entirely.
"verbatimModuleSyntax": true,
// A few notes:
// - Vue 3 supports ES2016+
// - For Vite, the actual compilation target is determined by the
// `build.target` option in the Vite config.
// So don't change the `target` field here. It has to be
// at least `ES2020` for dynamic `import()`s and `import.meta` to work correctly.
// - If you are not using Vite, feel free to overwrite the `target` field.
"target": "ESNext",
// For spec compilance.
// `true` by default if the `target` is `ES2020` or higher.
// Explicitly set it to `true` here in case some users want to overwrite the `target`.
"useDefineForClassFields": true,
// Recommended
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
// See <https://github.com/vuejs/vue-cli/pull/5688>
"skipLibCheck": true,
}
}
UsersGrid :
<template>
<div>
<ag-grid-vue :style="gridStyle" suppressHorizontalScroll="true" :gridOptions="gridOptions" :class="gridMode"
:columnDefs="columnDefs.value" :rowData="rowData" :loadingOverlayComponent="CardOverlay">
</ag-grid-vue>
</div>
</template>
<script setup lang="ts">
import { ref, watch, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router';
import { AgGridVue } from "ag-grid-vue3";
import { useI18n } from 'vue-i18n'
import { gridOptions, gridStyle } from "../../helpers/gridSettings";
import ActionButtons from '../../components/grids/ActionButtons.vue'
import CardOverlay from '../../components/general/CardOverlay.vue'
import Tr from "../../helpers/translation";
import useUser from '../../composables/useUser' // Could not find a declaration file for module '../../composables/useUser'. 'c:/Users/kaue.zuquetto/Desktop/Dev/Portal/resources/js/composables/useUser.js' implicitly has an 'any' type.
import { useStore } from 'vuex'
import type { User, ColDef } from '../../../../types'
const { t, locale } = useI18n()
const store = useStore()
const router = useRouter()
const gridMode = ref('ag-theme-alpine')
const columnDefs = ref()
const {
getAllData: getUsers,
deleteData: deleteUser,
rowData,
} = useUser();
const details = (id: number) => {
router.push(Tr.i18nRoute({ name: 'user.read', params: { id } }))
}
const i18nGrid = () => {
columnDefs.value = [
[],
{ headerName: 'ID', field: 'id', flex: 1 },
{ headerName: t('user.grid.colName'), field: 'name', flex: 3 },
{ headerName: t('user.grid.colEmail'), field: 'email', flex: 3 },
{ headerName: t('grid.created_at'), field: 'created_at', flex: 3 },
{ headerName: t('grid.updated_at'), field: 'updated_at', flex: 3 },
{
headerName: '',
flex: 1.5,
sortable: false,
filter: false,
cellRenderer: ActionButtons,
cellRendererParams: function () {
return {
details: details,
delete: deleteUser,
id: arguments[0].data.id,
buttons: ['details', 'delete']
}
},
},
] as ColDef<User>[];
}
const options = computed(() => {
return store.getters["preferences/options"]
})
watch(options, () => {
gridMode.value = store.state.preferences.options.grid
})
watch(locale, () => {
i18nGrid()
})
onMounted(() => {
i18nGrid()
getUsers()
gridMode.value = store.state.preferences.options.grid
})
</script>
my useUser composable :
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import useSwal from '../helpers/swal'
import Tr from '../helpers/translation'
import { gridOptions, gridStyle } from "../helpers/gridSettings"
const { confirm_delete_swal, toast_success, toast_error } = useSwal()
const useUser = (userID = null) => {
const router = useRouter()
const user = ref({})
const isLoading = ref(false)
const processing = ref(false)
const errors = ref(false)
const rowData = ref(null)
const getAllData = async () => {
try {
const response = await axios.get('/api/users')
const { status } = response
if (status === 200) {
rowData.value = response.data.data
rowData.value.sort((a, b) => b.id - a.id)
} else {
console.log('ERROR STATUS: ' + response.status)
toast_error()
}
} catch (e) {
gridOptions.api.hideOverlay()
gridOptions.api.showNoRowsOverlay()
toast_error()
console.log(e)
}
}
const getData = async () => {
try {
isLoading.value = true
const url = '/api/user/' + userID + '/read';
const response = await axios.get(url)
if (response.status === 200) {
const { data } = response.data
user.value = data
} else {
console.log('ERROR STATUS: ' + response.status)
toast_error()
}
} catch (e) {
toast_error()
console.log(e)
} finally {
isLoading.value = false
}
}
const createData = async (formData) => {
try {
processing.value = true
const response = await axios.post('/api/user/create', formData)
if (response.status === 200) {
const { data } = response.data
errors.value = {}
router.push(Tr.i18nRoute({ name: 'user.read', params: { id: data.id } }))
toast_success()
} else {
toast_error()
console.log('ERROR STATUS: ' + response.status)
}
} catch (e) {
toast_error()
console.log(e)
if (e.response && e.response.data.errors) {
errors.value = e.response.data.errors
}
} finally {
processing.value = false
}
}
const updateData = async (formData, userRoles) => {
try {
processing.value = true
const response = await axios.put('/api/user/' + userID + '/update', formData)
if (response.status === 200) {
const { data } = response.data
errors.value = {}
router.replace(Tr.i18nRoute({ name: 'user.read', params: { id: data } }))
userRoles.assignRoles()
} else {
toast_error()
console.log('ERROR STATUS: ' + response.status)
}
} catch (e) {
toast_error()
console.log(e)
toast_error()
if (e.response && e.response.data.errors) {
errors.value = e.response.data.errors
}
} finally {
processing.value = false
}
}
const deleteData = async (id) => {
try {
const { isConfirmed } = await confirm_delete_swal(id)
if (isConfirmed) {
const response = await axios.delete('/api/user/' + id + '/delete')
const { status } = response
if (status == 200) {
getAllData()
toast_success()
} else {
toast_error()
}
}
} catch (e) {
toast_error()
console.log(e)
if (e.response && e.response.data.errors) {
errors.value = e.response.data.errors
}
}
}
userID ? getData() : ''
return {
getAllData,
rowData,
getData,
user,
createData,
updateData,
deleteData,
isLoading,
processing,
errors,
}
}
export default useUser;
how can create a declaration file form my useUser hook ? its always complaining about not being a module, or not being callable
useUser.d.ts :
//Could not find a declaration file for module '../../composables/useUser'. implicitly has an 'any' type.ts(7016)
declare module 'useUser' {
export function useUser(): unknown
}
Unless otherwise configured, TypeScript files (or components) expect only typed imports. Your composable is a
.js
file. You can change this behavior and allow JS imports with the allowJs compiler option in tsconfigAlternatively, you can rewrite your composable in TypeScript.