Вопрос или проблема
Я пытаюсь вернуть созданный объект из POST-запроса на страницу. Всё работает, но сервер разработки Svelte Vite (который запущен) похоже не понимает типы должным образом, в результате мне приходится использовать any
, чтобы получить нужные данные.
У действия правильный выведенный тип возврата, когда я на него наводю курсор, но result
в колбэке улучшателя застрял с типом: ActionResult<Record<string, unknown> | undefined, Record<string, unknown> | undefined>
.
Вот соответствующее действие в +page.server.ts
:
export const actions: Actions = {
newPage: async ({ locals }) => {
if (locals.user == null) return fail(403);
const project = await getUserDefaultProject(locals.dbclient, locals.user.id);
if (project == null) return fail(500);
const newPage = await createNewPage(locals.dbclient, locals.user, project.id);
if (newPage) return newPage;
return fail(400);
},
};
(newPage
имеет тип <PageMetadata|null>
)
и код улучшенной формы в +page.svelte
:
<form
action="?/newPage"
method="POST"
use:enhance={({ cancel }) => {
if (!loggedIn.state || offlineMode.state) {
cancel();
createNewLocalPage();
return;
}
return async ({ result }) => {
if (result.status == 200) {
const newPage = (result as any).data as PageMetadata;
pages.state = [...pages.state, newPage];
}
};
}}
>
<button type="submit" title="создать новую страницу" id="create-new-page">
<Fa icon={faPlus} color="var(--background)" />
<div>новая страница</div>
</button>
</form>
Я искал, как правильно типизировать это, но ничего не нашёл, поэтому буду признателен за любую помощь.
Без минимально воспроизводимого кода я не могу дать вам очень полный ответ, но я полагаю, что замена if (result.status == 200)
на if (result.type === 'success')
для проверки вашего результата в улучшателе должна решить проблему, ваша форма будет выглядеть так:
<form
action="?/newPage"
method="POST"
use:enhance={({ cancel }) => {
if (!loggedIn.state || offlineMode.state) {
cancel();
createNewLocalPage();
return;
}
return async ({ result }) => {
if (result.type === 'success'){
const newPage = result.data;
pages.state = [...pages.state, newPage];
}
};
}}
>
<button type="submit" title="создать новую страницу" id="create-new-page">
<Fa icon={faPlus} color="var(--background)" />
<div>новая страница</div>
</button>
</form>
Объяснение: согласно документации причина, по которой вы можете получать ошибку, такую как Property 'data' does not exist on type 'ActionResult<Record<string, unknown> | undefined, Record<string, unknown> | undefined>'
, может быть связана с проверкой результата с result.status === 200
, потому что это удовлетворяет всем формам типу возврата, где некоторые не имеют свойства data
. Typescript примет поиск result.data
только в том случае, если ваша проверка гарантирует форму, в которой свойство существует; это будет result.type === 'success' || result.type === 'failure'
.
ActionResult
Когда вы вызываете действие формы через fetch, ответ будет одной из этих форм.<form method="post" use:enhance={() => { return ({ result }) => { // result имеет тип ActionResult }; }} type ActionResult< Success extends | Record<string, unknown> | undefined = Record<string, any>, Failure extends | Record<string, unknown> | undefined = Record<string, any> > = | { type: 'success'; status: number; data?: Success } | { type: 'failure'; status: number; data?: Failure } | { type: 'redirect'; status: number; location: string } | { type: 'error'; status?: number; error: any };
Ответ или решение
Работа с формами в SvelteKit может стать вызовом, особенно когда дело доходит до правильного типизирования ActionResults
. Давайте рассмотрим вашу проблему более подробно и найдем оптимальное решение.
Проблема: Неправильное понимание типов в ActionResults
Вы столкнулись с ситуацией, когда возвращаемый объект из POST-запроса к действию формы не типизируется должным образом. При попытке доступа к данным, возвращаемым из вашего действия, TypeScript ограничивает вас, указывая на тип ActionResult<Record<string, unknown> | undefined, Record<string, unknown> | undefined>
, что приводит к необходимости использовать any
для обойти эту проблему.
Решение: Правильное использование ActionResult
Ваш код в +page.server.ts
выглядит корректно:
export const actions: Actions = {
newPage: async ({ locals }) => {
if (locals.user == null) return fail(403);
const project = await getUserDefaultProject(locals.dbclient, locals.user.id);
if (project == null) return fail(500);
const newPage = await createNewPage(locals.dbclient, locals.user, project.id);
if (newPage) return newPage;
return fail(400);
},
};
Оптимизация: Изменение проверки результата
Как вы уже заметили, проблема с типами может быть решена изменением способа валидации результата в функции-обработчике use:enhance
. Вместо проверки result.status == 200
, вы можете использовать проверку result.type === 'success'
. Это более надёжный способ, так как каждая ветвь результата имеет более строгую типизацию:
<form
action="?/newPage"
method="POST"
use:enhance={({ cancel }) => {
if (!loggedIn.state || offlineMode.state) {
cancel();
createNewLocalPage();
return;
}
return async ({ result }) => {
if (result.type === 'success') {
const newPage = result.data;
pages.state = [...pages.state, newPage];
}
};
}}
>
<button type="submit" title="создать новую страницу" id="create-new-page">
<Fa icon={faPlus} color="var(--background)" />
<div>новая страница</div>
</button>
</form>
Почему это работает?
Используя result.type
, вы обеспечиваете TypeScript необходимой информацией о структуре результата. Различные типы результата (success
, failure
, redirect
, error
) имеют свои поля, и проверка result.type === 'success'
позволяет избежать неприятных ошибок. Если type
является success
, тогда data
гарантированно будет присутствовать, что позволяет вамtype указать более точный тип:
if (result.type === 'success') {
const newPage: PageMetadata = result.data; // теперь TypeScript понимает, что здесь находится PageMetadata
}
Заключение
Работа с типами в SvelteKit может быть сложной, но правильные проверки и использование встроенных возможностей TypeScript позволяет значительно упростить код и повысить его читаемость. Используя предложенные изменения, вы сможете избежать неясностей с типами и повысить качество вашего кода.
В случае дополнительных вопросов или сложностей с реализацией всегда можно обратиться к документации Svelte и TypeScript, которая может предоставить больше информации о том, как правильно работать с типами и обрабатывать различные результаты действий.