TypeScript断言函数是否需搭配try...catch?两种写法如何选择?
TypeScript断言函数在UI请求校验中的使用选择
我编写了一个TypeScript断言函数,在UI事件处理器中,将请求DTO传入API服务前会调用该函数。现有两种实现写法,想咨询:应该搭配try...catch语句使用断言函数并向用户展示错误信息?还是先通过if语句校验请求DTO的合法性,仅将断言函数用于通过TSC类型检查?
代码示例1:try/catch配合断言处理错误
function assertIsGetCampaignPackageReqDTO( value: any, ): asserts value is GetCampaignPackageRuquestDTO { if (!value.campaignId || !value.packageCode) { throw new Error('campaignId or packageCode is empty!'); } } const onSearchFormSubmit = async (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); try { assertIsGetCampaignPackageReqDTO(searchCondition); const { value } = await getCampaignPackageQuery.execute(searchCondition); if (value?.code === '0') { setCampaignPackages(campaignPackages.concat(value.result)); } } catch (error) { message.error(error.message); } };
代码示例2:先if校验,断言仅用于类型检查
function assertIsGetCampaignPackageReqDTO( value: any, ): asserts value is GetCampaignPackageRuquestDTO { if (!value.campaignId || !value.packageCode) { throw new Error('value is not GetCampaignPackageRuquestDTO'); } } const onSearchFormSubmit = async (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!searchCondition.campaignId || !searchCondition.packageCode) { return message.error('campaignId or packageCode is empty!'); } assertIsGetCampaignPackageReqDTO(searchCondition); const { value } = await getCampaignPackageQuery.execute(searchCondition); if (value?.code === '0') { setCampaignPackages(campaignPackages.concat(value.result)); } };
方案对比与建议
方案1的优缺点
- 优点:校验逻辑集中在断言函数中,无需在事件处理器中重复编写判断条件,代码更简洁。
- 缺点:
- 用异常处理来处理预期的用户输入错误,违背了异常的设计初衷(异常应用于处理意外的、不可预见的错误)。
- try/catch会捕获代码块内所有错误,包括后续API调用抛出的异常,导致用户看到的错误信息可能混淆输入校验错误和服务端错误,不利于问题定位。
方案2的优缺点
- 优点:
- 明确区分了预期错误(输入校验)和意外错误(API调用失败等),逻辑更清晰。
- 输入校验失败直接返回并提示用户,无需进入后续API调用流程,性能更优。
- 缺点:校验逻辑重复编写了两次(if判断和断言函数中),违反了DRY原则,后续修改校验规则时需要两处同步修改,容易出错。
优化后的推荐方案
将校验逻辑抽离为单独的类型守卫函数,同时让断言函数复用这个逻辑,既避免代码重复,又保持清晰的职责划分:
// 类型守卫函数:用于运行时校验并提供类型推断 function isGetCampaignPackageReqDTO(value: any): value is GetCampaignPackageRuquestDTO { return !!value.campaignId && !!value.packageCode; } // 断言函数:仅用于强制类型检查,依赖类型守卫的校验逻辑 function assertIsGetCampaignPackageReqDTO(value: any): asserts value is GetCampaignPackageRuquestDTO { if (!isGetCampaignPackageReqDTO(value)) { throw new Error('value is not GetCampaignPackageRuquestDTO'); } } // 事件处理器 const onSearchFormSubmit = async (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); // 用类型守卫做输入校验,提示用户 if (!isGetCampaignPackageReqDTO(searchCondition)) { return message.error('campaignId or packageCode is empty!'); } // 断言函数确保TSC类型检查通过,这里不会抛出预期错误(因为已经提前校验) assertIsGetCampaignPackageReqDTO(searchCondition); try { const { value } = await getCampaignPackageQuery.execute(searchCondition); if (value?.code === '0') { setCampaignPackages(campaignPackages.concat(value.result)); } } catch (apiError) { // 单独处理API调用的意外错误 message.error('请求失败,请稍后重试'); } };
最终结论
优先选择方案2的优化版本:
- 用类型守卫函数处理运行时的输入校验和用户提示,职责明确。
- 断言函数仅用于辅助TypeScript的类型推断,确保编译时类型安全,其抛出的异常仅作为开发阶段的兜底(比如代码逻辑错误导致未提前校验),而非用户可见的错误提示。
内容的提问来源于stack exchange,提问作者Lin Du
相关产品推荐
相关产品推荐

