refactor(契约): 补齐 80 个类型导出,前端不再手抄形状

契约原来有 184 个 schema 但只导出了 104 个类型,缺的那 80 个前端只能照着
手抄一遍 —— 这是「表格列静默空白」那一类 bug 的根因(d3348f9、2edb8cf,
以及上一个 commit 修的 Top100 两列)。现在 186 个 schema 对 186 个类型,
一一对应,下次要用直接 import。

补导出是机械的(fooSchema → Foo),零命名冲突。真正有价值的是换的过程中
契约逼出来的 5 处分歧 —— 手抄那份在说谎,而 vue-tsc 拦不住,因为类型说它是对的:

- Tutorial.createdBy 手抄成了可选的 `User`(即 AdminUser,带 email、
  rawPassword),后端下发的是必有的 SampleUser。读 createdBy.email 会拿到
  undefined。列表页因此被迫写 `row.createdBy?.username` 和 `row.createdAt!`,
  换成契约类型后两处断言都不需要了。
- TutorialListItem 手抄成 `Omit<Tutorial, "content">`,但后端列表接口连 code
  一起省了 —— 类型声称 code 在。
- Testcase 手抄成 `{input_name, output_name, score}`:响应实际有 5 个字段,
  且**没有 score**。score 是上传完成后前端按测试点数量平分补上去的,手抄那份
  把本地字段说成了响应字段。现在写成 `TestCaseEntry & { score: string }`。
- Tag 手抄成 `{id, name}`,契约是 `{id, name, problemCount}` —— shared/api.ts
  只好用 `Tag & { problemCount: number }` 把丢掉的补回来。
- CreateMessage 是旧后端按名字投递的形状(sender/recipient/submission),
  契约要的是 recipientId/submissionId。全仓零引用,删掉。

同时删掉另外两个零引用的手写类型:LANGUAGE_SHOW_LABEL、UserAdminType;
本地重复的 SampleUser 换成契约的;oj/problem/list.vue 里本地第三份 Tag 改成
`ContractTag & { checked: boolean }`。

需要收窄的一律**从契约派生再收窄**,字段名跟着契约走,只有真正本地的那一两个
键是自己的:

    export type Exercise = Omit<AdminExercise, "data"> & { data: 七种题型的联合 }
    export type SubmitCodePayload =
      Omit<CreateSubmissionRequest, "language"> & { language: LANGUAGE }

保留不动的窄化:StatisticInfo / SubmissionInfo(判题 JSONB 原文,snake_case)、
SQLDisplay*、ProblemFiltered(视图模型)、Exercise*Data(契约里 data 就是
Record<string, unknown>,七种题型结构不同,后端本来也不校验)。
types.ts 的手写 interface 从 31 个降到 22 个。

顺带修的代码:admin/tutorial/detail.vue 新建教程的表单对象缺三个后端产出的
字段,加了 TutorialEdit(对齐 BlankProblem / BlankContest 的写法);三处测试点
上传原来是拿响应对象原地塞 score,改成 map 出新对象,分数算法一字未改。

验证:apps/api tsc(7.0.2) 0 error、check:routes 168 条无遮蔽、
apps/web vue-tsc 0 error、vite build 通过。改动绝大部分在类型层,运行时只有
测试点上传那三处(等价替换)—— **那条路径要传 zip 才能实跑,没有实打**,
只做了代码等价性核对。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 18:07:01 -06:00
parent 78a2fb4fce
commit d3b05b8629
18 changed files with 193 additions and 105 deletions

View File

@@ -181,14 +181,16 @@ async function upload() {
const file = new File([blob], "testcase.zip", { type: "application/zip" }) const file = new File([blob], "testcase.zip", { type: "application/zip" })
const res = await uploadTestcases(file, { sql: true }) const res = await uploadTestcases(file, { sql: true })
const testcases: Testcase[] = res.data.info // score 不在上传响应里,是这里按测试点数量平分补上的(余数给最后一个)
const baseScore = Math.floor(100 / testcases.length) const entries = res.data.info
const remainder = 100 - baseScore * testcases.length const baseScore = Math.floor(100 / entries.length)
testcases.forEach((tc, i) => { const remainder = 100 - baseScore * entries.length
tc.score = String( const testcases: Testcase[] = entries.map((entry, i) => ({
i === testcases.length - 1 ? baseScore + remainder : baseScore, ...entry,
) score: String(
}) i === entries.length - 1 ? baseScore + remainder : baseScore,
),
}))
emit("uploaded", res.data.id, testcases) emit("uploaded", res.data.id, testcases)
message.success("上传成功") message.success("上传成功")

View File

@@ -169,14 +169,16 @@ async function upload() {
const file = new File([blob], "testcase.zip", { type: "application/zip" }) const file = new File([blob], "testcase.zip", { type: "application/zip" })
const res = await uploadTestcases(file) const res = await uploadTestcases(file)
const testcases: Testcase[] = res.data.info // score 不在上传响应里,是这里按测试点数量平分补上的(余数给最后一个)
const baseScore = Math.floor(100 / testcases.length) const entries = res.data.info
const remainder = 100 - baseScore * testcases.length const baseScore = Math.floor(100 / entries.length)
testcases.forEach((tc, i) => { const remainder = 100 - baseScore * entries.length
tc.score = String( const testcases: Testcase[] = entries.map((entry, i) => ({
i === testcases.length - 1 ? baseScore + remainder : baseScore, ...entry,
) score: String(
}) i === entries.length - 1 ? baseScore + remainder : baseScore,
),
}))
emit("uploaded", res.data.id, testcases) emit("uploaded", res.data.id, testcases)
message.success("上传成功") message.success("上传成功")

View File

@@ -294,10 +294,12 @@ async function handleUploadTestcases({ file }: UploadCustomRequestOptions) {
message.error("上传测试用例失败") message.error("上传测试用例失败")
return return
} }
const testcases = res.data.info // score 不在上传响应里,前端按测试点数量平分补上
for (let file of testcases) { const entries = res.data.info
file.score = (100 / testcases.length).toFixed(0) const testcases: Testcase[] = entries.map((entry) => ({
} ...entry,
score: (100 / entries.length).toFixed(0),
}))
problem.value.testCaseScore = testcases problem.value.testCaseScore = testcases
problem.value.testCaseId = res.data.id problem.value.testCaseId = res.data.id
} catch (err) { } catch (err) {

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import CodeEditor from "shared/components/CodeEditor.vue" import CodeEditor from "shared/components/CodeEditor.vue"
import MarkdownEditor from "shared/components/MarkdownEditor.vue" import MarkdownEditor from "shared/components/MarkdownEditor.vue"
import type { Tutorial } from "utils/types" import type { TutorialEdit } from "utils/types"
import { createTutorial, getTutorial, updateTutorial } from "../api" import { createTutorial, getTutorial, updateTutorial } from "../api"
import ExerciseManager from "./components/ExerciseManager.vue" import ExerciseManager from "./components/ExerciseManager.vue"
@@ -13,7 +13,7 @@ const route = useRoute()
const message = useMessage() const message = useMessage()
const props = defineProps<Props>() const props = defineProps<Props>()
const tutorial = reactive<Tutorial>({ const tutorial = reactive<TutorialEdit>({
id: 0, id: 0,
title: "", title: "",
content: "", content: "",

View File

@@ -4,7 +4,7 @@ import { NFlex, NTag } from "naive-ui"
import { useRouteQuery } from "@vueuse/router" import { useRouteQuery } from "@vueuse/router"
import { getProblemList } from "oj/api" import { getProblemList } from "oj/api"
import { getTagColor } from "utils/functions" import { getTagColor } from "utils/functions"
import type { ProblemFiltered } from "utils/types" import type { ProblemFiltered, Tag as ContractTag } from "utils/types"
import { getProblemTagList } from "shared/api" import { getProblemTagList } from "shared/api"
import Hitokoto from "shared/components/Hitokoto.vue" import Hitokoto from "shared/components/Hitokoto.vue"
import Pagination from "shared/components/Pagination.vue" import Pagination from "shared/components/Pagination.vue"
@@ -16,11 +16,8 @@ import ProblemStatus from "./components/ProblemStatus.vue"
import AuthorSelect from "shared/components/AuthorSelect.vue" import AuthorSelect from "shared/components/AuthorSelect.vue"
import ProblemListTitle from "./components/ProblemListTitle.vue" import ProblemListTitle from "./components/ProblemListTitle.vue"
interface Tag { // 列表页的标签是个视图模型:契约的标签 + 本地的选中态
id: number type Tag = ContractTag & { checked: boolean }
name: string
checked: boolean
}
interface ProblemQuery { interface ProblemQuery {
keyword: string keyword: string

View File

@@ -34,7 +34,7 @@ export async function getProfile(
} }
export function getProblemTagList() { export function getProblemTagList() {
return api2.get<Array<Tag & { problemCount: number }>>("problem-tags") return api2.get<Tag[]>("problem-tags")
} }
export function getHitokoto() { export function getHitokoto() {

View File

@@ -1,4 +1,3 @@
import { LANGUAGE_SHOW_VALUE } from "./constants"
import type { import type {
AdminProblem as ContractAdminProblem, AdminProblem as ContractAdminProblem,
SubmissionDetail, SubmissionDetail,
@@ -9,6 +8,7 @@ import type {
SessionUser, SessionUser,
UserProfile, UserProfile,
EmbeddedSubmission as ContractEmbeddedSubmission, EmbeddedSubmission as ContractEmbeddedSubmission,
Message as ContractMessage,
Grade, Grade,
ProblemDetail, ProblemDetail,
ProblemDifficulty, ProblemDifficulty,
@@ -32,9 +32,6 @@ export interface AcmProblemsStatus {
} }
} }
export type UserAdminType =
"Regular User" | "Student Admin" | "Teacher Admin" | "Super Admin"
/** /**
* 后台用户管理里的用户。`rawPassword` 是明文密码,只有超管专属接口下发 —— * 后台用户管理里的用户。`rawPassword` 是明文密码,只有超管专属接口下发 ——
* 老师要能查学生密码,见契约 adminUserSchema 的注释。 * 老师要能查学生密码,见契约 adminUserSchema 的注释。
@@ -86,24 +83,17 @@ export interface SQLDisplay {
| { changed_tables: SQLDisplayTable[] } | { changed_tables: SQLDisplayTable[] }
} }
export type LANGUAGE_SHOW_LABEL =
(typeof LANGUAGE_SHOW_VALUE)[keyof typeof LANGUAGE_SHOW_VALUE]
export type SUBMISSION_RESULT = export type SUBMISSION_RESULT =
-2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 -2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
export type ProblemStatus = "passed" | "failed" | "not_test" export type ProblemStatus = "passed" | "failed" | "not_test"
interface SampleUser {
id: number
username: string
realName: string | null
}
export interface Tag { /**
id: number * 题目标签。用契约的 —— 它比手抄那份多一个 `problemCount`
name: string * shared/api.ts 原来还得用 `Tag & { problemCount: number }` 把它补回来。
} */
export type { Tag } from "@oj2/contract"
export type { export type {
AdminTag, AdminTag,
@@ -113,16 +103,18 @@ export type {
GenerateSqlTestCaseResponse, GenerateSqlTestCaseResponse,
} from "@oj2/contract" } from "@oj2/contract"
export interface TestcaseUploadedReturns { /**
id: string * 上传测试点的返回。取契约 —— 手抄那份少了三个字段
info: Testcase[] * stripped_output_md5 / input_size / output_size。这套键名保持 snake_case
} * 是因为它会原样落进 problem.test_case_score 和判题沙箱读的 info 文件。
*/
export type { UploadTestCaseResponse as TestcaseUploadedReturns } from "@oj2/contract"
export interface Testcase { /**
input_name: string * 题目表单里的测试点:上传返回的条目 + 前端本地算出的分值。
output_name: string * `score` **不在响应里** —— 是上传完成后按测试点数量平分补上去的。
score: string */
} export type Testcase = TestCaseEntry & { score: string }
/** /**
* 题目详情。以契约的 ProblemDetail 为准,只在这里补两处前端自己的窄化: * 题目详情。以契约的 ProblemDetail 为准,只在这里补两处前端自己的窄化:
@@ -275,11 +267,9 @@ export interface Code {
value: string value: string
} }
export interface SubmitCodePayload { /** 提交代码的请求体。取契约的形状,只把 language 收窄成前端的 LANGUAGE 联合 */
problemId: number export type SubmitCodePayload = Omit<CreateSubmissionRequest, "language"> & {
language: LANGUAGE language: LANGUAGE
code: string
contestId?: number
} }
// ==================== 流程图相关类型 ==================== // ==================== 流程图相关类型 ====================
@@ -465,52 +455,56 @@ export interface Announcement extends AnnouncementEdit {
/** 列表不下发正文:公告是 8MB 上限的富文本,列表页只显示标题 */ /** 列表不下发正文:公告是 8MB 上限的富文本,列表页只显示标题 */
export type AnnouncementListItem = Omit<Announcement, "content"> export type AnnouncementListItem = Omit<Announcement, "content">
export interface Message { /**
id: number * 站内信。取契约的形状,只把 `submission` 换成前端窄化过的那个
sender: SampleUser * statisticInfo / language 在契约里是 unknown见 EmbeddedSubmission
createTime: string */
message: string export type Message = Omit<ContractMessage, "submission"> & {
submission: EmbeddedSubmission submission: EmbeddedSubmission
} }
export interface CreateMessage { /**
sender: string * 题目表情。三个类型都直接取自契约 —— 语义 key 必须与后端 reaction/models.py
recipient: string * 的 ReactionType 一致(见根 CLAUDE.md手抄一份迟早对不上。
submission: string *
message: string * 注意 `ReactionCounts` 是 Partial 的:后端只下发有票的类型,没人投的键不出现。
} */
export type {
ReactionKey,
ReactionCounts,
ReactionState,
} from "@oj2/contract"
import type { ReactionKey, SampleUser } from "@oj2/contract"
export type ReactionKey = /**
| "too_easy" * 教程。直接取契约 —— 手抄的那份把 `createdBy` 写成了可选的 `User`(后端下发的是
| "too_hard" * SampleUser`createdAt` / `updatedAt` 也写成了可选,列表页因此被迫写
| "confusing" * `row.createdBy?.username` 和 `row.createdAt!`。
| "buggy" *
| "learned" * 列表项也用契约的:它比 `Omit<Tutorial, "content">` **还少一个 code** ——
| "interesting" * 后端列表接口连 code 一起省了,手抄那份声称它在。
| "want_explain" */
export type {
AdminTutorial as Tutorial,
AdminTutorialListItem as TutorialListItem,
} from "@oj2/contract"
import type {
AdminExercise,
AdminTutorial,
CreateSubmissionRequest,
TestCaseEntry,
} from "@oj2/contract"
export type ReactionCounts = Record<ReactionKey, number> /**
* 教程编辑表单。只留可编辑字段 —— createdBy / createdAt / updatedAt 由后端产出,
export interface ReactionState { * 新建时压根不存在(对齐 BlankProblem / BlankContest 的写法)。
mine: ReactionKey | null *
counts: ReactionCounts | null * `code` 收窄成 string读回来时统一 `?? ""`,代码编辑器的 v-model 不接受 null
} */
export type TutorialEdit = Omit<
export interface Tutorial { AdminTutorial,
id: number "createdBy" | "createdAt" | "updatedAt" | "code"
title: string > & { code: string }
content: string
code: string
isPublic: boolean
order: number
type: "python" | "c"
createdBy?: User
updatedAt?: string
createdAt?: string
}
/** 后台教程列表不下发正文:教程正文是整篇 markdown列表只排序和切换可见性 */
export type TutorialListItem = Omit<Tutorial, "content">
export interface ExerciseMcqData { export interface ExerciseMcqData {
question: string question: string
@@ -555,12 +549,13 @@ export interface ExerciseGroupData {
answer: number[] answer: number[]
} }
export type ExerciseType = export type { ExerciseType } from "@oj2/contract"
"mcq" | "sort" | "fill" | "match" | "predict" | "debug" | "group"
export interface Exercise { /**
id: number * 练习题。契约里 `data` 是 Record<string, unknown>(各题型结构不同,后端不校验),
type: ExerciseType * 前端按题型收窄成判别联合 —— 组件靠它区分七种题型的字段。
*/
export type Exercise = Omit<AdminExercise, "data"> & {
data: data:
| ExerciseMcqData | ExerciseMcqData
| ExerciseSortData | ExerciseSortData
@@ -569,7 +564,6 @@ export interface Exercise {
| ExercisePredictData | ExercisePredictData
| ExerciseDebugData | ExerciseDebugData
| ExerciseGroupData | ExerciseGroupData
order: number
} }
export type { export type {

View File

@@ -67,3 +67,5 @@ export type UserRank = z.infer<typeof userRankSchema>
export type MyRank = z.infer<typeof myRankSchema> export type MyRank = z.infer<typeof myRankSchema>
export type ActivityRankItem = z.infer<typeof activityRankItemSchema> export type ActivityRankItem = z.infer<typeof activityRankItemSchema>
export type Metrics = z.infer<typeof metricsSchema> export type Metrics = z.infer<typeof metricsSchema>
export type PublicProfile = z.infer<typeof publicProfileSchema>

View File

@@ -67,3 +67,5 @@ export type AchievementSummary = z.infer<typeof achievementSummarySchema>
export type AchievementRarity = z.infer<typeof achievementRaritySchema> export type AchievementRarity = z.infer<typeof achievementRaritySchema>
export type AchievementRarityStat = z.infer<typeof achievementRarityStatSchema> export type AchievementRarityStat = z.infer<typeof achievementRarityStatSchema>
export type AchievementNotification = z.infer<typeof achievementNotificationSchema> export type AchievementNotification = z.infer<typeof achievementNotificationSchema>
export type MarkAchievementsRead = z.infer<typeof markAchievementsReadSchema>

View File

@@ -668,3 +668,58 @@ export type BatchProblemTagResponse = z.infer<typeof batchProblemTagResponseSche
export type SqlTestCaseScript = z.infer<typeof sqlTestCaseScriptSchema> export type SqlTestCaseScript = z.infer<typeof sqlTestCaseScriptSchema>
export type GenerateSqlTestCaseResponse = z.infer<typeof generateSqlTestCaseResponseSchema> export type GenerateSqlTestCaseResponse = z.infer<typeof generateSqlTestCaseResponseSchema>
export type AdminProblemSetProgress = z.infer<typeof adminProblemSetProgressSchema> export type AdminProblemSetProgress = z.infer<typeof adminProblemSetProgressSchema>
export type AdminAnnouncementList = z.infer<typeof adminAnnouncementListSchema>
export type CreateAnnouncementRequest = z.infer<typeof createAnnouncementRequestSchema>
export type UpdateAnnouncementRequest = z.infer<typeof updateAnnouncementRequestSchema>
export type TutorialType = z.infer<typeof tutorialTypeSchema>
export type AdminTutorial = z.infer<typeof adminTutorialSchema>
export type AdminTutorialListItem = z.infer<typeof adminTutorialListItemSchema>
export type AdminTutorialGroups = z.infer<typeof adminTutorialGroupsSchema>
export type CreateTutorialRequest = z.infer<typeof createTutorialRequestSchema>
export type UpdateTutorialRequest = z.infer<typeof updateTutorialRequestSchema>
export type SetTutorialVisibilityRequest = z.infer<typeof setTutorialVisibilityRequestSchema>
export type ExerciseType = z.infer<typeof exerciseTypeSchema>
export type AdminExercise = z.infer<typeof adminExerciseSchema>
export type CreateExerciseRequest = z.infer<typeof createExerciseRequestSchema>
export type UpdateExerciseRequest = z.infer<typeof updateExerciseRequestSchema>
export type ToggleAiReportPinResponse = z.infer<typeof toggleAiReportPinResponseSchema>
export type AchievementOperator = z.infer<typeof achievementOperatorSchema>
export type CreateAchievementRequest = z.infer<typeof createAchievementRequestSchema>
export type UpdateAchievementRequest = z.infer<typeof updateAchievementRequestSchema>
export type UpdateUserRequest = z.infer<typeof updateUserRequestSchema>
export type ImportUsersRequest = z.infer<typeof importUsersRequestSchema>
export type DeleteUsersRequest = z.infer<typeof deleteUsersRequestSchema>
export type ResetPasswordResponse = z.infer<typeof resetPasswordResponseSchema>
export type UpdateWebsiteConfigRequest = z.infer<typeof updateWebsiteConfigRequestSchema>
export type UpdateJudgeServerRequest = z.infer<typeof updateJudgeServerRequestSchema>
export type UploadImageResponse = z.infer<typeof uploadImageResponseSchema>
export type CreateContestRequest = z.infer<typeof createContestRequestSchema>
export type UpdateContestRequest = z.infer<typeof updateContestRequestSchema>
export type UpdateAcmHelperRequest = z.infer<typeof updateAcmHelperRequestSchema>
export type ProblemSetDifficulty = z.infer<typeof problemSetDifficultySchema>
export type ProblemSetStatus = z.infer<typeof problemSetStatusSchema>
export type BadgeConditionType = z.infer<typeof badgeConditionTypeSchema>
export type AdminProblemSet = z.infer<typeof adminProblemSetSchema>
export type AdminProblemSetList = z.infer<typeof adminProblemSetListSchema>
export type CreateProblemSetRequest = z.infer<typeof createProblemSetRequestSchema>
export type UpdateProblemSetRequest = z.infer<typeof updateProblemSetRequestSchema>
export type UpdateProblemSetStatusRequest = z.infer<typeof updateProblemSetStatusRequestSchema>
export type AdminProblemSetProblem = z.infer<typeof adminProblemSetProblemSchema>
export type AddProblemToSetRequest = z.infer<typeof addProblemToSetRequestSchema>
export type UpdateProblemInSetRequest = z.infer<typeof updateProblemInSetRequestSchema>
export type AdminProblemSetBadge = z.infer<typeof adminProblemSetBadgeSchema>
export type CreateProblemSetBadgeRequest = z.infer<typeof createProblemSetBadgeRequestSchema>
export type UpdateProblemSetBadgeRequest = z.infer<typeof updateProblemSetBadgeRequestSchema>
export type RenameTagRequest = z.infer<typeof renameTagRequestSchema>
export type BatchProblemTagRequest = z.infer<typeof batchProblemTagRequestSchema>
export type AcTrendYear = z.infer<typeof acTrendYearSchema>
export type GenerateFlowchartRequest = z.infer<typeof generateFlowchartRequestSchema>
export type GenerateFlowchartResponse = z.infer<typeof generateFlowchartResponseSchema>
export type UpdateProblemRequest = z.infer<typeof updateProblemRequestSchema>
export type MakeProblemPublicRequest = z.infer<typeof makeProblemPublicRequestSchema>
export type AddContestProblemRequest = z.infer<typeof addContestProblemRequestSchema>
export type TestCaseEntry = z.infer<typeof testCaseEntrySchema>
export type UploadTestCaseResponse = z.infer<typeof uploadTestCaseResponseSchema>
export type SqlPreviewRequest = z.infer<typeof sqlPreviewRequestSchema>
export type GenerateSqlTestCaseRequest = z.infer<typeof generateSqlTestCaseRequestSchema>

View File

@@ -109,3 +109,8 @@ export type AiDetail = z.infer<typeof aiDetailSchema>
export type HeatmapItem = z.infer<typeof heatmapItemSchema> export type HeatmapItem = z.infer<typeof heatmapItemSchema>
export type AiAnalysisRecord = z.infer<typeof aiAnalysisRecordSchema> export type AiAnalysisRecord = z.infer<typeof aiAnalysisRecordSchema>
export type LoginSummary = z.infer<typeof loginSummarySchema> export type LoginSummary = z.infer<typeof loginSummarySchema>
export type AiAnalysisRequest = z.infer<typeof aiAnalysisRequestSchema>
export type AiHintRequest = z.infer<typeof aiHintRequestSchema>
export type ClassAnalysisRequest = z.infer<typeof classAnalysisRequestSchema>
export type ClassPkAnalysisRequest = z.infer<typeof classPkAnalysisRequestSchema>

View File

@@ -67,3 +67,6 @@ export type ClassRankItem = z.infer<typeof classRankItemSchema>
export type ClassUserRank = z.infer<typeof classUserRankSchema> export type ClassUserRank = z.infer<typeof classUserRankSchema>
export type ClassComparison = z.infer<typeof classComparisonSchema> export type ClassComparison = z.infer<typeof classComparisonSchema>
export type ClassComparisonResponse = z.infer<typeof classComparisonResponseSchema> export type ClassComparisonResponse = z.infer<typeof classComparisonResponseSchema>
export type ClassUserRankItem = z.infer<typeof classUserRankItemSchema>
export type ClassComparisonRequest = z.infer<typeof classComparisonRequestSchema>

View File

@@ -19,3 +19,5 @@ export function paginatedSchema<T extends z.ZodType>(item: T) {
} }
export type SampleUser = z.infer<typeof sampleUserSchema> export type SampleUser = z.infer<typeof sampleUserSchema>
export type PaginationQuery = z.infer<typeof paginationQuerySchema>

View File

@@ -77,3 +77,12 @@ export type Message = z.infer<typeof messageSchema>
export type MessageList = z.infer<typeof messageListSchema> export type MessageList = z.infer<typeof messageListSchema>
export type Announcement = z.infer<typeof announcementSchema> export type Announcement = z.infer<typeof announcementSchema>
export type TutorialSummary = z.infer<typeof tutorialSummarySchema> export type TutorialSummary = z.infer<typeof tutorialSummarySchema>
export type AnnouncementList = z.infer<typeof announcementListSchema>
export type CreateMessageRequest = z.infer<typeof createMessageRequestSchema>
export type ReactionKey = z.infer<typeof reactionKeySchema>
export type ReactionCounts = z.infer<typeof reactionCountsSchema>
export type ReactionState = z.infer<typeof reactionStateSchema>
export type SetReactionRequest = z.infer<typeof setReactionRequestSchema>
export type Tutorial = z.infer<typeof tutorialSchema>
export type Exercise = z.infer<typeof exerciseSchema>

View File

@@ -46,3 +46,7 @@ export type ContestList = z.infer<typeof contestListSchema>
export type ContestRankItem = z.infer<typeof contestRankItemSchema> export type ContestRankItem = z.infer<typeof contestRankItemSchema>
export type ContestRank = z.infer<typeof contestRankSchema> export type ContestRank = z.infer<typeof contestRankSchema>
export type ContestAccess = z.infer<typeof contestAccessSchema> export type ContestAccess = z.infer<typeof contestAccessSchema>
export type ContestStatus = z.infer<typeof contestStatusSchema>
export type ContestPasswordRequest = z.infer<typeof contestPasswordRequestSchema>
export type ContestProblems = z.infer<typeof contestProblemsSchema>

View File

@@ -96,3 +96,5 @@ export type FlowchartCurrent = z.infer<typeof flowchartCurrentSchema>
export type FlowchartDetail = z.infer<typeof flowchartDetailSchema> export type FlowchartDetail = z.infer<typeof flowchartDetailSchema>
export type CreateFlowchartResponse = z.infer<typeof createFlowchartResponseSchema> export type CreateFlowchartResponse = z.infer<typeof createFlowchartResponseSchema>
export type CreateFlowchartRequest = z.infer<typeof createFlowchartRequestSchema> export type CreateFlowchartRequest = z.infer<typeof createFlowchartRequestSchema>
export type FlowchartStatus = z.infer<typeof flowchartStatusSchema>

View File

@@ -107,3 +107,7 @@ export type ProblemSetProgress = z.infer<typeof problemSetProgressSchema>
export type ProblemSetProgressList = z.infer<typeof problemSetProgressListSchema> export type ProblemSetProgressList = z.infer<typeof problemSetProgressListSchema>
export type UserBadge = z.infer<typeof userBadgeSchema> export type UserBadge = z.infer<typeof userBadgeSchema>
export type CompletedProblem = z.infer<typeof completedProblemSchema> export type CompletedProblem = z.infer<typeof completedProblemSchema>
export type ProblemSetUserProgressSummary = z.infer<typeof problemSetUserProgressSummarySchema>
export type JoinProblemSetRequest = z.infer<typeof joinProblemSetRequestSchema>
export type UpdateProblemSetProgressRequest = z.infer<typeof updateProblemSetProgressRequestSchema>

View File

@@ -146,3 +146,6 @@ export type SubmissionList = z.infer<typeof submissionListSchema>
export type EmbeddedSubmission = z.infer<typeof embeddedSubmissionSchema> export type EmbeddedSubmission = z.infer<typeof embeddedSubmissionSchema>
export type CreateSubmissionResponse = z.infer<typeof createSubmissionResponseSchema> export type CreateSubmissionResponse = z.infer<typeof createSubmissionResponseSchema>
export type FormatCodeResponse = z.infer<typeof formatCodeResponseSchema> export type FormatCodeResponse = z.infer<typeof formatCodeResponseSchema>
export type ShareSubmissionRequest = z.infer<typeof shareSubmissionRequestSchema>
export type FormatCodeRequest = z.infer<typeof formatCodeRequestSchema>