diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2025-07-06 19:36:11 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-06 19:36:11 +0900 |
| commit | a8abb03d1785791ab40e57ab49c87640914532c9 (patch) | |
| tree | f80ea7a393a278e29f9642e86be8b341fcb4b95b /packages/frontend/src/utility | |
| parent | Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop (diff) | |
| download | misskey-a8abb03d1785791ab40e57ab49c87640914532c9.tar.gz misskey-a8abb03d1785791ab40e57ab49c87640914532c9.tar.bz2 misskey-a8abb03d1785791ab40e57ab49c87640914532c9.zip | |
refactor(frontend): Formまわりの型強化 (#16260)
* refactor(frontend): Formまわりの型強化
* fix
* avoid non-null assertion and add null check for safety
* refactor
* avoid non-null assertion and add null check for safety
* Update clip.vue
---------
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src/utility')
| -rw-r--r-- | packages/frontend/src/utility/form.ts | 122 | ||||
| -rw-r--r-- | packages/frontend/src/utility/get-note-menu.ts | 2 | ||||
| -rw-r--r-- | packages/frontend/src/utility/get-user-menu.ts | 2 |
3 files changed, 85 insertions, 41 deletions
diff --git a/packages/frontend/src/utility/form.ts b/packages/frontend/src/utility/form.ts index 1032e97ac9..2b765dc714 100644 --- a/packages/frontend/src/utility/form.ts +++ b/packages/frontend/src/utility/form.ts @@ -5,55 +5,59 @@ import * as Misskey from 'misskey-js'; -type EnumItem = string | { +export type EnumItem = string | { label: string; - value: string; + value: unknown; }; type Hidden = boolean | ((v: any) => boolean); -export type FormItem = { +interface FormItemBase { label?: string; + hidden?: Hidden; +} + +export interface StringFormItem extends FormItemBase { type: 'string'; default?: string | null; description?: string; required?: boolean; - hidden?: Hidden; multiline?: boolean; treatAsMfm?: boolean; -} | { - label?: string; +} + +export interface NumberFormItem extends FormItemBase { type: 'number'; default?: number | null; description?: string; required?: boolean; - hidden?: Hidden; step?: number; -} | { - label?: string; +} + +export interface BooleanFormItem extends FormItemBase { type: 'boolean'; default?: boolean | null; description?: string; - hidden?: Hidden; -} | { - label?: string; +} + +export interface EnumFormItem extends FormItemBase { type: 'enum'; default?: string | null; required?: boolean; - hidden?: Hidden; enum: EnumItem[]; -} | { - label?: string; +} + +export interface RadioFormItem extends FormItemBase { type: 'radio'; default?: unknown | null; required?: boolean; - hidden?: Hidden; options: { label: string; value: unknown; }[]; -} | { - label?: string; +} + +export interface RangeFormItem extends FormItemBase { type: 'range'; default?: number | null; description?: string; @@ -62,42 +66,80 @@ export type FormItem = { min: number; max: number; textConverter?: (value: number) => string; - hidden?: Hidden; -} | { - label?: string; +} + +export interface ObjectFormItem extends FormItemBase { type: 'object'; default?: Record<string, unknown> | null; - hidden: Hidden; -} | { - label?: string; +} + +export interface ArrayFormItem extends FormItemBase { type: 'array'; default?: unknown[] | null; - hidden: Hidden; -} | { +} + +export interface ButtonFormItem extends FormItemBase { type: 'button'; content?: string; - hidden?: Hidden; action: (ev: MouseEvent, v: any) => void; -} | { +} + +export interface DriveFileFormItem extends FormItemBase { type: 'drive-file'; defaultFileId?: string | null; - hidden?: Hidden; validate?: (v: Misskey.entities.DriveFile) => Promise<boolean>; -}; +} + +export type FormItem = + StringFormItem | + NumberFormItem | + BooleanFormItem | + EnumFormItem | + RadioFormItem | + RangeFormItem | + ObjectFormItem | + ArrayFormItem | + ButtonFormItem | + DriveFileFormItem; export type Form = Record<string, FormItem>; +export type FormItemWithDefault = FormItem & { + default: unknown; +}; + +export type FormWithDefault = Record<string, FormItemWithDefault>; + +type GetRadioItemType<Item extends RadioFormItem = RadioFormItem> = Item['options'][number]['value']; +type GetEnumItemType<Item extends EnumFormItem, E = Item['enum'][number]> = E extends { value: unknown } ? E['value'] : E; + +type InferDefault<T, Fallback> = T extends { default: infer D } + ? D extends undefined ? Fallback : D + : Fallback; + +type NonNullableIfRequired<T, Item extends FormItem> = + Item extends { required: false } ? T | null | undefined : NonNullable<T>; + type GetItemType<Item extends FormItem> = - Item['type'] extends 'string' ? string : - Item['type'] extends 'number' ? number : - Item['type'] extends 'boolean' ? boolean : - Item['type'] extends 'radio' ? unknown : - Item['type'] extends 'range' ? number : - Item['type'] extends 'enum' ? string : - Item['type'] extends 'array' ? unknown[] : - Item['type'] extends 'object' ? Record<string, unknown> : - Item['type'] extends 'drive-file' ? Misskey.entities.DriveFile | undefined : - never; + Item extends StringFormItem + ? NonNullableIfRequired<InferDefault<Item, string>, Item> + : Item extends NumberFormItem + ? NonNullableIfRequired<InferDefault<Item, number>, Item> + : Item extends BooleanFormItem + ? boolean + : Item extends RadioFormItem + ? GetRadioItemType<Item> + : Item extends RangeFormItem + ? NonNullableIfRequired<InferDefault<RangeFormItem, number>, Item> + : Item extends EnumFormItem + ? GetEnumItemType<Item> + : Item extends ArrayFormItem + ? NonNullableIfRequired<InferDefault<ArrayFormItem, unknown[]>, Item> + : Item extends ObjectFormItem + ? NonNullableIfRequired<InferDefault<Item, Record<string, unknown>>, Item> + : Item extends DriveFileFormItem + ? Misskey.entities.DriveFile | undefined + : never; export type GetFormResultType<F extends Form> = { [P in keyof F]: GetItemType<F[P]>; diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts index ea93444f08..5361c1252d 100644 --- a/packages/frontend/src/utility/get-note-menu.ts +++ b/packages/frontend/src/utility/get-note-menu.ts @@ -101,7 +101,7 @@ export async function getNoteClipMenu(props: { const { canceled, result } = await os.form(i18n.ts.createNewClip, { name: { type: 'string', - default: null, + default: null as string | null, label: i18n.ts.name, }, description: { diff --git a/packages/frontend/src/utility/get-user-menu.ts b/packages/frontend/src/utility/get-user-menu.ts index 5c08b8c462..ad0864019b 100644 --- a/packages/frontend/src/utility/get-user-menu.ts +++ b/packages/frontend/src/utility/get-user-menu.ts @@ -132,6 +132,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router const userDetailed = await misskeyApi('users/show', { userId: user.id, }); + const { canceled, result } = await os.form(i18n.ts.editMemo, { memo: { type: 'string', @@ -141,6 +142,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router default: userDetailed.memo, }, }); + if (canceled) return; os.apiWithDialog('users/update-memo', { |