From 4f39875786cf48dd9ab599c19e7666dc9768a75a Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Fri, 30 Jan 2026 19:03:37 +0000 Subject: [PATCH] TypeScript check and typing corrections. --- frontend/src/App.vue | 4 ++-- frontend/src/components/BreadCrumb.vue | 4 ++-- frontend/src/components/DownloadButton.vue | 4 ++-- frontend/src/components/FileExplorer.vue | 6 +++--- frontend/src/components/FileSize.vue | 2 +- frontend/src/components/Gallery.vue | 6 +++--- frontend/src/components/HeaderSelected.vue | 2 +- frontend/src/components/MediaPreview.vue | 2 +- frontend/src/components/UploadButton.vue | 2 +- frontend/src/components/UserManagementModal.vue | 2 +- frontend/src/repositories/Document.ts | 9 +++++++-- frontend/src/stores/main.ts | 2 +- frontend/src/utils/docsort.ts | 2 +- frontend/src/utils/index.ts | 2 +- 14 files changed, 27 insertions(+), 22 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 5f99389..4c8fa2b 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -38,10 +38,10 @@ interface Path { const store = useMainStore() const path: ComputedRef = computed(() => { const p = decodeURIComponent(Router.currentRoute.value.path).split('//') - const pathList = p[0].split('/').filter(value => value !== '') + const pathList = (p[0] ?? '').split('/').filter(value => value !== '') const query = p.slice(1).join('//') return { - path: p[0], + path: p[0] ?? '', pathList, query } diff --git a/frontend/src/components/BreadCrumb.vue b/frontend/src/components/BreadCrumb.vue index ee3b34b..9b33fab 100644 --- a/frontend/src/components/BreadCrumb.vue +++ b/frontend/src/components/BreadCrumb.vue @@ -54,7 +54,7 @@ const isCurrent = (index: number) => index == props.path.length ? 'location' : u const focusCurrent = () => { nextTick(() => { const index = props.path.length - if (index < links.length) links[index].focus() + if (index < links.length) links[index]!.focus() }) } @@ -63,7 +63,7 @@ const navigate = (index: number) => { if (!link) throw Error(`No link at index ${index} (path: ${props.path})`) const url = index ? `/${longest.value.slice(0, index).join('/')}/` : '/' const long = longest.value.length ? `/${longest.value.join('/')}/` : '/' - const browser = decodeURIComponent(location.hash.slice(1).split('//')[0]) + const browser = decodeURIComponent(location.hash.slice(1).split('//')[0] ?? '') const u = url.replaceAll('?', '%3F').replaceAll('#', '%23') // Clicking on current link clears the rest of the path and adds new history if (isCurrent(index)) { longest.value.splice(index); router.push(u) } diff --git a/frontend/src/components/DownloadButton.vue b/frontend/src/components/DownloadButton.vue index c8e6ab5..623ee10 100644 --- a/frontend/src/components/DownloadButton.vue +++ b/frontend/src/components/DownloadButton.vue @@ -145,7 +145,7 @@ const download = async () => { if (files.length === 1) { store.selected.clear() store.error = "Single file via browser downloads" - return linkdl(`/files/${files[0][1]}`) + return linkdl(`/files/${files[0]![1]}`) } // Use FileSystem API if multiple files and the browser supports it if ('showDirectoryPicker' in window) { @@ -164,7 +164,7 @@ const download = async () => { } // Otherwise, zip and download console.log("Falling back to zip download") - const name = sel.keys.length === 1 ? sel.docs[sel.keys[0]].name : 'download' + const name = sel.keys.length === 1 ? sel.docs[sel.keys[0]!]!.name : 'download' linkdl(`/zip/${Array.from(sel.keys).join('+')}/${name}.zip`) store.error = "Downloading as ZIP via browser downloads" store.selected.clear() diff --git a/frontend/src/components/FileExplorer.vue b/frontend/src/components/FileExplorer.vue index 658afca..a9dca10 100644 --- a/frontend/src/components/FileExplorer.vue +++ b/frontend/src/components/FileExplorer.vue @@ -175,7 +175,7 @@ defineExpose({ let [begin, end] = d > 0 ? [index, moveto] : [moveto, index] for (let p = begin; p !== end; p = increment(p, 1)) { if (p === N) continue - const key = docs[p].key + const key = docs[p]!.key if (store.selected.has(key)) store.selected.delete(key) else store.selected.add(key) } @@ -255,8 +255,8 @@ const mkdir = (doc: Doc, name: string) => { } const showFolderBreadcrumb = (i: number) => { const docs = props.documents - const docloc = docs[i].loc - return i === 0 ? docloc !== loc.value : docloc !== docs[i - 1].loc + const docloc = docs[i]!.loc + return i === 0 ? docloc !== loc.value : docloc !== docs[i - 1]!.loc } const selectionIndeterminate = computed({ get: () => { diff --git a/frontend/src/components/FileSize.vue b/frontend/src/components/FileSize.vue index 688efb1..432d342 100644 --- a/frontend/src/components/FileSize.vue +++ b/frontend/src/components/FileSize.vue @@ -7,7 +7,7 @@ import { Doc } from '@/repositories/Document' import { computed } from 'vue' const sizeClass = computed(() => { - const unit = props.doc.sizedisp.split('\u202F').slice(-1)[0] + const unit = props.doc.sizedisp.split('\u202F').slice(-1)[0]! return +unit ? "bytes" : unit }) diff --git a/frontend/src/components/Gallery.vue b/frontend/src/components/Gallery.vue index e0b27af..e41418b 100644 --- a/frontend/src/components/Gallery.vue +++ b/frontend/src/components/Gallery.vue @@ -129,7 +129,7 @@ defineExpose({ let [begin, end] = d > 0 ? [index, moveto] : [moveto, index] for (let p = begin; p !== end; p = increment(p, 1)) { if (p === N) continue - const key = docs[p].key + const key = docs[p]!.key if (store.selected.has(key)) store.selected.delete(key) else store.selected.add(key) } @@ -209,8 +209,8 @@ const mkdir = (doc: Doc, name: string) => { } const showFolderBreadcrumb = (i: number) => { const docs = props.documents - const docloc = docs[i].loc - return i === 0 ? docloc !== loc.value : docloc !== docs[i - 1].loc + const docloc = docs[i]!.loc + return i === 0 ? docloc !== loc.value : docloc !== docs[i - 1]!.loc } diff --git a/frontend/src/components/HeaderSelected.vue b/frontend/src/components/HeaderSelected.vue index acab35b..72049af 100644 --- a/frontend/src/components/HeaderSelected.vue +++ b/frontend/src/components/HeaderSelected.vue @@ -26,7 +26,7 @@ const op = (op: string, dst?: string) => { const msg = { op, sel: sel.keys.map(key => { - const doc = sel.docs[key] + const doc = sel.docs[key]! return doc.loc ? `${doc.loc}/${doc.name}` : doc.name }) } diff --git a/frontend/src/components/MediaPreview.vue b/frontend/src/components/MediaPreview.vue index 10d6ec1..d5614f0 100644 --- a/frontend/src/components/MediaPreview.vue +++ b/frontend/src/components/MediaPreview.vue @@ -46,7 +46,7 @@ const next = () => { let el: HTMLAudioElement | HTMLVideoElement | null = null for (const i in medias) { if (medias[i] === (fscurrent || media.value)) { - el = medias[+i + 1] || medias[0] + el = medias[+i + 1] ?? medias[0] ?? null break } } diff --git a/frontend/src/components/UploadButton.vue b/frontend/src/components/UploadButton.vue index b5ba25d..33d142d 100644 --- a/frontend/src/components/UploadButton.vue +++ b/frontend/src/components/UploadButton.vue @@ -205,7 +205,7 @@ const WSCreate = async () => await new Promise(resolve => { const worker = async () => { const ws = await WSCreate() while (upqueue.length) { - const f = upqueue[0] + const f = upqueue[0]! const start = f.cloudPos const end = Math.min(f.file.size, start + (1<<20)) const control = { name: f.cloudName, size: f.file.size, start, end } diff --git a/frontend/src/components/UserManagementModal.vue b/frontend/src/components/UserManagementModal.vue index 9411b42..3075568 100644 --- a/frontend/src/components/UserManagementModal.vue +++ b/frontend/src/components/UserManagementModal.vue @@ -179,7 +179,7 @@ const deleteUserAction = async (username: string) => { const copySuccess = async (isButtonClick: boolean = false) => { const passwordMatch = success.value.match(/(?:Password|New password): (.+)/) if (passwordMatch) { - await navigator.clipboard.writeText(passwordMatch[1]) + await navigator.clipboard.writeText(passwordMatch[1]!) if (isButtonClick) { // Show "Copied!" indication on button copyButtonText.value = '✅ Copied!' diff --git a/frontend/src/repositories/Document.ts b/frontend/src/repositories/Document.ts index 7796bdd..7c736aa 100644 --- a/frontend/src/repositories/Document.ts +++ b/frontend/src/repositories/Document.ts @@ -12,15 +12,20 @@ export type DocProps = { } export class Doc { - private _name: string = "" public loc: string = "" public key: FUID = "" public size: number = 0 public mtime: number = 0 public haystack: string = "" public dir: boolean = false + /** @internal Use the name getter/setter instead */ + public _name: string = "" - constructor(props: Partial = {}) { Object.assign(this, props) } + constructor(props: Partial = {}) { + const { name, ...rest } = props + Object.assign(this, rest) + if (name) this.name = name // Use setter for validation + } get name() { return this._name } set name(name: string) { if (name.includes('/') || name.startsWith('.')) throw Error(`Invalid name: ${name}`) diff --git a/frontend/src/stores/main.ts b/frontend/src/stores/main.ts index 6ba61c4..00ce15b 100644 --- a/frontend/src/stores/main.ts +++ b/frontend/src/stores/main.ts @@ -124,7 +124,7 @@ export const useMainStore = defineStore('main', { ret.recursive.push([rel, full, doc]) } for (const key of ret.keys) { - const base = ret.docs[key] + const base = ret.docs[key]! const basepath = base.loc ? `${base.loc}/${base.name}` : base.name const nremove = base.loc.length add(base.name, basepath, base) diff --git a/frontend/src/utils/docsort.ts b/frontend/src/utils/docsort.ts index 6c67158..bfa08f4 100644 --- a/frontend/src/utils/docsort.ts +++ b/frontend/src/utils/docsort.ts @@ -41,7 +41,7 @@ export const sortedGrouped = (documents: Doc[], order: SortOrder) => { // Find the "best" item in each folder (first after sorting = best according to criteria) const folderBest = new Map() for (const [folder, docs] of byFolder) { - folderBest.set(folder, docs[0]) + folderBest.set(folder, docs[0]!) } // Sort folders: by path for name sort, by best item for modified/size diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index 4df8b8f..1c1e2ea 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -70,7 +70,7 @@ export function getFileType(name: string): string { const dotIndex = name.lastIndexOf('.') if (dotIndex === -1 || dotIndex === name.length - 1) return 'unknown' const ext = name.slice(dotIndex + 1).toLowerCase() - return Object.keys(filetypes).find(type => filetypes[type].includes(ext)) || 'unknown' + return Object.keys(filetypes).find(type => filetypes[type]!.includes(ext)) || 'unknown' } // Prebuilt for fast & consistent sorting