From f79420886270ac8b36ed14b36e78ee5d8f7ad779 Mon Sep 17 00:00:00 2001 From: Philipp Fischbeck Date: Sun, 16 Jan 2022 03:38:11 +0100 Subject: [PATCH] Fix more typing issues (#928) * Fix or comment several ts-ignores * Fix typing related to BaseOverflowButton * Remove unused functionality of useCookbooks, fix usage bug * Fix more typing, add some comments * Only allow ts-ignore if it has a comment --- frontend/.eslintrc.js | 7 +++- frontend/api/admin/admin-groups.ts | 4 +- frontend/api/admin/admin-users.ts | 29 +------------ .../api/class-interfaces/recipes/recipe.ts | 4 +- .../api/class-interfaces/recipes/types.ts | 2 +- frontend/api/class-interfaces/tools.ts | 20 ++------- frontend/api/class-interfaces/utils.ts | 7 +++- .../Domain/Recipe/RecipeDialogShare.vue | 4 +- .../Domain/Recipe/RecipeIngredients.vue | 2 +- .../Domain/Recipe/RecipeInstructions.vue | 2 +- .../components/Domain/Recipe/RecipeNotes.vue | 5 ++- .../Domain/Recipe/RecipePrintView.vue | 2 +- .../components/Domain/Recipe/RecipeTools.vue | 4 +- .../components/global/BaseButtonGroup.vue | 2 +- frontend/components/global/BaseDialog.vue | 4 -- .../components/global/BaseOverflowButton.vue | 16 ++++---- frontend/components/global/MarkdownEditor.vue | 2 +- .../components/global/RecipeJsonEditor.vue | 2 +- .../composables/recipes/use-recipe-meta.ts | 3 +- frontend/composables/recipes/use-recipes.ts | 10 ++--- frontend/composables/use-backups.ts | 4 +- frontend/composables/use-group-cookbooks.ts | 27 +----------- frontend/composables/use-group-mealplan.ts | 6 +-- frontend/composables/use-router.ts | 3 +- frontend/composables/use-utils.ts | 6 +-- frontend/layouts/default.vue | 1 - frontend/pages/admin/manage/groups/_id.vue | 16 ++------ frontend/pages/admin/manage/users/_id.vue | 20 ++------- frontend/pages/admin/parser.vue | 29 ++++++------- frontend/pages/admin/site-settings.vue | 9 ++-- frontend/pages/recipe/_slug/cook.vue | 2 +- frontend/pages/recipe/_slug/index.vue | 41 ++++++++----------- .../pages/recipe/_slug/ingredient-parser.vue | 1 - frontend/pages/recipe/create.vue | 29 ++++--------- frontend/pages/recipes/all.vue | 2 - frontend/pages/recipes/categories/_slug.vue | 1 - frontend/pages/recipes/tags/_slug.vue | 1 - frontend/pages/recipes/tools/_slug.vue | 1 - frontend/pages/register.vue | 3 +- frontend/pages/search.vue | 7 ++-- frontend/pages/shared/recipes/_id.vue | 12 ++---- frontend/pages/shopping-lists/_id.vue | 5 +-- frontend/pages/user/group/data/migrations.vue | 3 +- frontend/pages/user/group/data/recipes.vue | 10 ++--- frontend/pages/user/profile/edit.vue | 5 +-- 45 files changed, 126 insertions(+), 249 deletions(-) diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 9789bf40..0bd03f4a 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -42,8 +42,13 @@ module.exports = { allowModifiers: true, }, ], + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "ts-ignore": "allow-with-description", + }, + ], // TODO Gradually activate all rules - "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/explicit-module-boundary-types": "off", diff --git a/frontend/api/admin/admin-groups.ts b/frontend/api/admin/admin-groups.ts index d374a2f7..d8781fc8 100644 --- a/frontend/api/admin/admin-groups.ts +++ b/frontend/api/admin/admin-groups.ts @@ -1,5 +1,5 @@ import { BaseCRUDAPI } from "../_base"; -import { UserRead } from "./admin-users"; +import { UserOut } from "~/types/api-types/user"; const prefix = "/api"; export interface Token { @@ -29,7 +29,7 @@ export interface GroupRead extends GroupCreate { id: number; categories: any[]; webhooks: any[]; - users: UserRead[]; + users: UserOut[]; preferences: Preferences; } diff --git a/frontend/api/admin/admin-users.ts b/frontend/api/admin/admin-users.ts index 3f15d5ef..f93eac79 100644 --- a/frontend/api/admin/admin-users.ts +++ b/frontend/api/admin/admin-users.ts @@ -1,39 +1,14 @@ import { BaseCRUDAPI } from "../_base"; +import { UserIn, UserOut } from "~/types/api-types/user"; const prefix = "/api"; -export interface UserCreate { - username: string; - fullName: string; - email: string; - admin: boolean; - group: string; - advanced: boolean; - canInvite: boolean; - canManage: boolean; - canOrganize: boolean; - password: string; -} - -export interface UserToken { - name: string; - id: number; - createdAt: Date; -} - -export interface UserRead extends UserToken { - id: number; - groupId: number; - favoriteRecipes: any[]; - tokens: UserToken[]; -} - const routes = { adminUsers: `${prefix}/admin/users`, adminUsersId: (tag: string) => `${prefix}/admin/users/${tag}`, }; -export class AdminUsersApi extends BaseCRUDAPI { +export class AdminUsersApi extends BaseCRUDAPI { baseRoute: string = routes.adminUsers; itemRoute = routes.adminUsersId; } diff --git a/frontend/api/class-interfaces/recipes/recipe.ts b/frontend/api/class-interfaces/recipes/recipe.ts index 93d467e7..43daaca8 100644 --- a/frontend/api/class-interfaces/recipes/recipe.ts +++ b/frontend/api/class-interfaces/recipes/recipe.ts @@ -53,7 +53,6 @@ export class RecipeAPI extends BaseCRUDAPI { async createAsset(recipeSlug: string, payload: CreateAsset) { const formData = new FormData(); - // @ts-ignore formData.append("file", payload.file); formData.append("name", payload.name); formData.append("extension", payload.extension); @@ -65,8 +64,7 @@ export class RecipeAPI extends BaseCRUDAPI { updateImage(slug: string, fileObject: File) { const formData = new FormData(); formData.append("image", fileObject); - // @ts-ignore - formData.append("extension", fileObject.name.split(".").pop()); + formData.append("extension", fileObject.name.split(".").pop() ?? ""); return this.requests.put(routes.recipesRecipeSlugImage(slug), formData); } diff --git a/frontend/api/class-interfaces/recipes/types.ts b/frontend/api/class-interfaces/recipes/types.ts index d4014beb..a7099719 100644 --- a/frontend/api/class-interfaces/recipes/types.ts +++ b/frontend/api/class-interfaces/recipes/types.ts @@ -46,7 +46,7 @@ export interface CreateAsset { name: string; icon: string; extension: string; - file?: File; + file: File; } export interface RecipeCommentCreate { diff --git a/frontend/api/class-interfaces/tools.ts b/frontend/api/class-interfaces/tools.ts index 21762d99..f0b8e92e 100644 --- a/frontend/api/class-interfaces/tools.ts +++ b/frontend/api/class-interfaces/tools.ts @@ -1,33 +1,19 @@ import { BaseCRUDAPI } from "../_base"; -import { Recipe } from "~/types/api-types/recipe"; +import { RecipeTool, RecipeToolCreate, RecipeToolResponse } from "~/types/api-types/recipe"; const prefix = "/api"; -export interface CreateTool { - name: string; - onHand: boolean; -} - -export interface Tool extends CreateTool { - id: number; - slug: string; -} - -export interface RecipeToolResponse extends Tool { - recipes: Recipe[]; -} - const routes = { tools: `${prefix}/tools`, toolsId: (id: string) => `${prefix}/tools/${id}`, toolsSlug: (id: string) => `${prefix}/tools/slug/${id}`, }; -export class ToolsApi extends BaseCRUDAPI { +export class ToolsApi extends BaseCRUDAPI { baseRoute: string = routes.tools; itemRoute = routes.toolsId; async byslug(slug: string) { - return await this.requests.get(routes.toolsSlug(slug)); + return await this.requests.get(routes.toolsSlug(slug)); } } diff --git a/frontend/api/class-interfaces/utils.ts b/frontend/api/class-interfaces/utils.ts index a3c457f4..4aa93d27 100644 --- a/frontend/api/class-interfaces/utils.ts +++ b/frontend/api/class-interfaces/utils.ts @@ -2,15 +2,18 @@ import { BaseAPI } from "../_base"; const prefix = "/api"; +interface DownloadData { + fileToken: string, +} + export class UtilsAPI extends BaseAPI { async download(url: string) { - const { response } = await this.requests.get(url); + const { response } = await this.requests.get(url); if (!response) { return; } - // @ts-ignore const token: string = response.data.fileToken; const tokenURL = prefix + "/utils/download?token=" + token; diff --git a/frontend/components/Domain/Recipe/RecipeDialogShare.vue b/frontend/components/Domain/Recipe/RecipeDialogShare.vue index 90891325..8b979b5c 100644 --- a/frontend/components/Domain/Recipe/RecipeDialogShare.vue +++ b/frontend/components/Domain/Recipe/RecipeDialogShare.vue @@ -22,7 +22,7 @@ v-on="on" > - + @@ -178,4 +178,4 @@ export default defineComponent({ }; }, }); - \ No newline at end of file + diff --git a/frontend/components/Domain/Recipe/RecipeIngredients.vue b/frontend/components/Domain/Recipe/RecipeIngredients.vue index eb8ad959..3a35eadd 100644 --- a/frontend/components/Domain/Recipe/RecipeIngredients.vue +++ b/frontend/components/Domain/Recipe/RecipeIngredients.vue @@ -25,7 +25,7 @@ diff --git a/frontend/pages/recipe/_slug/ingredient-parser.vue b/frontend/pages/recipe/_slug/ingredient-parser.vue index 64020ca4..76b5333f 100644 --- a/frontend/pages/recipe/_slug/ingredient-parser.vue +++ b/frontend/pages/recipe/_slug/ingredient-parser.vue @@ -135,7 +135,6 @@ export default defineComponent({ if (data) { parsedIng.value = data; - // @ts-ignore errors.value = data.map((ing, index: number) => { const unitError = !checkForUnit(ing.ingredient.unit); const foodError = !checkForFood(ing.ingredient.food); diff --git a/frontend/pages/recipe/create.vue b/frontend/pages/recipe/create.vue index 05bef746..cf552907 100644 --- a/frontend/pages/recipe/create.vue +++ b/frontend/pages/recipe/create.vue @@ -332,7 +332,8 @@ import RecipeCategoryTagSelector from "~/components/Domain/Recipe/RecipeCategory import { validators } from "~/composables/use-validators"; import { Recipe } from "~/types/api-types/recipe"; import { alert } from "~/composables/use-toast"; -import { VForm} from "~/types/vuetify"; +import { VForm } from "~/types/vuetify"; +import { MenuItem } from "~/components/global/BaseOverflowButton.vue"; export default defineComponent({ components: { VJsoneditor, RecipeCategoryTagSelector }, @@ -344,7 +345,7 @@ export default defineComponent({ const { $globals } = useContext(); - const tabs = [ + const tabs: MenuItem[] = [ { icon: $globals.icons.link, text: "Import with URL", @@ -422,48 +423,36 @@ export default defineComponent({ // =================================================== // Recipe URL Import - // @ts-ignore - const domUrlForm = ref(null); + const domUrlForm = ref(null); async function createByUrl(url: string) { - if (!domUrlForm.value.validate() || url === "") { + if (!domUrlForm.value?.validate() || url === "") { console.log("Invalid URL", url); return; } state.loading = true; const { response } = await api.recipes.createOneByUrl(url); - if (response?.status !== 201) { - // @ts-ignore - if (!response?.error?.response?.data?.detail?.message) { - state.error = true; - } - - state.loading = false; - return; - } handleResponse(response); } // =================================================== // Recipe Create By Name const newRecipeName = ref(""); - // @ts-ignore - const domCreateByName = ref(null); + const domCreateByName = ref(null); async function createByName(name: string) { - if (!domCreateByName.value.validate() || name === "") { + if (!domCreateByName.value?.validate() || name === "") { return; } const { response } = await api.recipes.createOne({ name }); // TODO createOne claims to return a Recipe, but actually the API only returns a string - // @ts-ignore + // @ts-ignore See above handleResponse(response, true); } // =================================================== // Recipe Import From Zip File - // @ts-ignore - const newRecipeZip = ref(null); + const newRecipeZip = ref(null); const newRecipeZipFileName = "archive"; async function createByZip() { diff --git a/frontend/pages/recipes/all.vue b/frontend/pages/recipes/all.vue index 1f3b8997..d4ba4f4e 100644 --- a/frontend/pages/recipes/all.vue +++ b/frontend/pages/recipes/all.vue @@ -47,9 +47,7 @@ export default defineComponent({ }, 500); function removeRecipe(slug: string) { - // @ts-ignore for (let i = 0; i < recipes?.value?.length; i++) { - // @ts-ignore if (recipes?.value[i].slug === slug) { recipes?.value.splice(i, 1); break; diff --git a/frontend/pages/recipes/categories/_slug.vue b/frontend/pages/recipes/categories/_slug.vue index 2302f4a8..6594f09a 100644 --- a/frontend/pages/recipes/categories/_slug.vue +++ b/frontend/pages/recipes/categories/_slug.vue @@ -115,7 +115,6 @@ export default defineComponent({ methods: { assignSorted(val: Array) { if (this.category) { - // @ts-ignore this.category.recipes = val; } }, diff --git a/frontend/pages/recipes/tags/_slug.vue b/frontend/pages/recipes/tags/_slug.vue index fc849a4c..813d1c45 100644 --- a/frontend/pages/recipes/tags/_slug.vue +++ b/frontend/pages/recipes/tags/_slug.vue @@ -115,7 +115,6 @@ export default defineComponent({ methods: { assignSorted(val: Array) { if (this.tags) { - // @ts-ignore this.tags.recipes = val; } }, diff --git a/frontend/pages/recipes/tools/_slug.vue b/frontend/pages/recipes/tools/_slug.vue index 49dd5eab..212c6dbe 100644 --- a/frontend/pages/recipes/tools/_slug.vue +++ b/frontend/pages/recipes/tools/_slug.vue @@ -109,7 +109,6 @@ export default defineComponent({ methods: { assignSorted(val: Array) { if (this.tools) { - // @ts-ignore this.tools.recipes = val; } }, diff --git a/frontend/pages/register.vue b/frontend/pages/register.vue index 210933aa..63600e79 100644 --- a/frontend/pages/register.vue +++ b/frontend/pages/register.vue @@ -129,8 +129,7 @@ export default defineComponent({ state.joinGroup = true; } - // @ts-ignore - const domRegisterForm = ref(null); + const domRegisterForm = ref(null); const form = reactive({ group: "", diff --git a/frontend/pages/search.vue b/frontend/pages/search.vue index 0b92901d..baaf23e4 100644 --- a/frontend/pages/search.vue +++ b/frontend/pages/search.vue @@ -188,7 +188,7 @@ export default defineComponent({ const includesTags = check( state.includeTags, - // @ts-ignore + // @ts-ignore See above recipe.tags.map((x: Tag) => x.name), state.tagFilter.matchAny, state.tagFilter.exclude @@ -196,8 +196,7 @@ export default defineComponent({ const includesCats = check( state.includeCategories, - // @ts-ignore - + // @ts-ignore See above recipe.recipeCategory.map((x) => x.name), state.catFilter.matchAny, state.catFilter.exclude @@ -205,7 +204,7 @@ export default defineComponent({ const includesFoods = check( state.includeFoods, - // @ts-ignore + // @ts-ignore See above recipe.recipeIngredient.map((x) => x?.food?.name || ""), state.foodFilter.matchAny, state.foodFilter.exclude diff --git a/frontend/pages/shared/recipes/_id.vue b/frontend/pages/shared/recipes/_id.vue index b1de494b..42660ff4 100644 --- a/frontend/pages/shared/recipes/_id.vue +++ b/frontend/pages/shared/recipes/_id.vue @@ -270,7 +270,7 @@ import { useMeta, useRoute, } from "@nuxtjs/composition-api"; -// @ts-ignore +// @ts-ignore vue-markdown has no types import VueMarkdown from "@adapttive/vue-markdown"; // import { useRecipeMeta } from "~/composables/recipes"; import { useStaticRoutes, useUserApi } from "~/composables/api"; @@ -316,7 +316,6 @@ export default defineComponent({ }, }); - // @ts-ignore const { recipeImage } = useStaticRoutes(); const { meta, title } = useMeta(); @@ -330,28 +329,25 @@ export default defineComponent({ meta.value = [ { hid: "og:title", property: "og:title", content: data.name ?? "" }, - // @ts-ignore { hid: "og:desc", property: "og:description", - content: data.description, + content: data.description ?? "", }, { hid: "og-image", property: "og:image", content: imageURL ?? "", }, - // @ts-ignore { hid: "twitter:title", property: "twitter:title", - content: data.name, + content: data.name ?? "", }, - // @ts-ignore { hid: "twitter:desc", property: "twitter:description", - content: data.description, + content: data.description ?? "", }, { hid: "t-type", name: "twitter:card", content: "summary_large_image" }, ]; diff --git a/frontend/pages/shopping-lists/_id.vue b/frontend/pages/shopping-lists/_id.vue index bf333af0..956521f1 100644 --- a/frontend/pages/shopping-lists/_id.vue +++ b/frontend/pages/shopping-lists/_id.vue @@ -425,7 +425,7 @@ export default defineComponent({ shoppingList.value?.listItems.forEach((item) => { if (item.labelId) { labels.push({ - // @ts-ignore + // @ts-ignore TODO name: item.label.name, id: item.labelId, }); @@ -439,7 +439,7 @@ export default defineComponent({ const items: { [prop: string]: ShoppingListItemCreate[] } = {}; const noLabel = { - "No Label": [], + "No Label": [] as ShoppingListItemCreate[], }; shoppingList.value?.listItems.forEach((item) => { @@ -450,7 +450,6 @@ export default defineComponent({ items[item.label.name] = [item]; } } else { - // @ts-ignore noLabel["No Label"].push(item); } }); diff --git a/frontend/pages/user/group/data/migrations.vue b/frontend/pages/user/group/data/migrations.vue index 8cc413be..763fdd2c 100644 --- a/frontend/pages/user/group/data/migrations.vue +++ b/frontend/pages/user/group/data/migrations.vue @@ -68,6 +68,7 @@ import { defineComponent, reactive, toRefs, useContext, computed, onMounted } from "@nuxtjs/composition-api"; import { SupportedMigration } from "~/api/class-interfaces/group-migrations"; import { ReportSummary } from "~/api/class-interfaces/group-reports"; +import { MenuItem } from "~/components/global/BaseOverflowButton.vue"; import { useUserApi } from "~/composables/api"; const MIGRATIONS = { @@ -92,7 +93,7 @@ export default defineComponent({ reports: [] as ReportSummary[], }); - const items = [ + const items: MenuItem[] = [ { text: "Nextcloud", value: MIGRATIONS.nextcloud, diff --git a/frontend/pages/user/group/data/recipes.vue b/frontend/pages/user/group/data/recipes.vue index 8f7da554..c2c9480b 100644 --- a/frontend/pages/user/group/data/recipes.vue +++ b/frontend/pages/user/group/data/recipes.vue @@ -162,6 +162,7 @@ import { useRecipes, allRecipes } from "~/composables/recipes"; import { Recipe } from "~/types/api-types/recipe"; import GroupExportData from "~/components/Domain/Group/GroupExportData.vue"; import { GroupDataExport } from "~/api/class-interfaces/recipe-bulk-actions"; +import { MenuItem } from "~/components/global/BaseOverflowButton.vue"; const MODES = { tag: "tag", @@ -191,7 +192,7 @@ export default defineComponent({ id: false, owner: false, tags: true, - tools: "Tools", + tools: true, categories: true, recipeYield: false, dateAdded: false, @@ -207,29 +208,25 @@ export default defineComponent({ dateAdded: "Date Added", }; - const actions = [ + const actions: MenuItem[] = [ { icon: $globals.icons.database, text: "Export", - value: 0, event: "export-selected", }, { icon: $globals.icons.tags, text: "Tag", - value: 1, event: "tag-selected", }, { icon: $globals.icons.tags, text: "Categorize", - value: 2, event: "categorize-selected", }, { icon: $globals.icons.delete, text: "Delete", - value: 3, event: "delete-selected", }, ]; @@ -264,7 +261,6 @@ export default defineComponent({ // All Recipes function selectAll() { - // @ts-ignore selected.value = allRecipes.value; } diff --git a/frontend/pages/user/profile/edit.vue b/frontend/pages/user/profile/edit.vue index 914e8b10..4e4751a1 100644 --- a/frontend/pages/user/profile/edit.vue +++ b/frontend/pages/user/profile/edit.vue @@ -116,6 +116,7 @@ import { ref, reactive, defineComponent, computed, useContext, watch } from "@nu import { useUserApi } from "~/composables/api"; import UserAvatar from "~/components/Domain/User/UserAvatar.vue"; import { VForm } from "~/types/vuetify"; +import { UserOut } from "~/types/api-types/user"; export default defineComponent({ components: { @@ -123,7 +124,7 @@ export default defineComponent({ }, setup() { const nuxtContext = useContext(); - const user = computed(() => nuxtContext.$auth.user); + const user = computed(() => nuxtContext.$auth.user as unknown as UserOut); watch(user, () => { userCopy.value = { ...user.value }; @@ -141,7 +142,6 @@ export default defineComponent({ }); async function updateUser() { - // @ts-ignore const { response } = await api.users.updateOne(userCopy.value.id, userCopy.value); if (response?.status === 200) { nuxtContext.$auth.fetchUser(); @@ -152,7 +152,6 @@ export default defineComponent({ if (!userCopy.value?.id) { return; } - // @ts-ignore const { response } = await api.users.changePassword(userCopy.value.id, { currentPassword: password.current, newPassword: password.newOne,