安大略省的《残障人士无障碍法案》(AODA,Accessibility for Ontarians with Disabilities Act)要求该省的公共机构和大型私营企业确保其网站符合 WCAG 2.1 AA 级别的无障碍标准。对开发团队而言,这意味着每一次设计变更、每一次组件迭代都需要经过规范化的无障碍审查。如果审查流程只依赖人工记忆和零散的文档,很容易出现漏项、标准不统一、审查结果难以追溯等问题。将 AODA 相关的审查规则用 TypeScript 的类型系统封装起来,可以让审查数据结构在编译期就得到约束,配合自动化检查脚本,能够显著提升无障碍合规工作的工程化程度。本文将从类型建模的角度,完整介绍如何封装一套 AODA 设计审查的类型体系。

一、理解 AODA 对设计审查的核心要求
AODA 本身是一部法律框架,其技术层面的落地标准由 WCAG(Web Content Accessibility Guidelines)承担。AODA 要求大多数组织在 2021 年之后达到 WCAG 2.1 AA 级别,具体涉及感知性、可操作性、可理解性和健壮性四大原则。对应到设计审查场景,常见的检查项包括:文本与背景的色彩对比度是否达到 4.5:1、所有交互元素是否可以通过键盘访问、图片是否提供了有意义的替代文本、表单控件是否具有正确的标签关联、页面结构是否使用了语义化地标等。
在设计审查类型之前,需要先把这些要求归纳为可结构化的数据模型。一个审查项至少要包含:规则编号(例如 WCAG 对应的 SC 编号)、规则描述、适用范围、严重级别以及检查方式。把这些字段抽象成 TypeScript 类型后,审查团队提交的每一份审查结果都必须遵循统一的结构,避免不同审查人员使用不同格式记录问题。
另一个关键点是严重级别的划分。业界通常参考 ARIA 的做法,将问题分为 critical、serious、moderate 和 minor 四级。critical 级别的问题(如按钮无法通过键盘聚焦)会直接阻止残障用户完成任务,必须在上线前修复;而 minor 级别的问题(如对比度略低于推荐值)可以排入后续迭代。在类型中明确区分这些级别,有助于自动化流程根据级别决定是否阻断构建。
二、定义审查项与检查结果的基础类型
首先定义规则编号和严重级别这两个字面量联合类型,这是整个类型体系的基石:
// WCAG 2.1 AA 级别中与设计审查最相关的部分成功标准编号
export type WcagRuleId =
| '1.1.1' // 非文本内容替代
| '1.3.1' // 信息与关系(语义化结构)
| '1.4.3' // 对比度(最低要求)
| '1.4.11' // 非文本对比度
| '2.1.1' // 键盘可访问
| '2.4.7' // 焦点可见
| '3.3.2' // 标签或说明
| '4.1.2' // 名称、角色、值;
export type SeverityLevel = 'critical' | 'serious' | 'moderate' | 'minor';
// 审查项:描述一条可检查的 AODA 合规规则
export interface AuditChecklistItem {
ruleId: WcagRuleId;
title: string;
description: string;
severity: SeverityLevel;
scope: 'global' | 'component' | 'page';
}字面量联合类型的优势在于约束力强且提示友好。当审查人员在填写 severity 字段时输入了 blocker 这样的非标准值,TypeScript 编译器会立即报错并提示所有合法选项。相比用 string 类型随意填写,这种约束从源头保证了数据的规范性。
接下来定义单次检查的结果类型。检查结果与审查项是分离的:审查项是静态规则,检查结果则是针对某个具体页面或组件的动态数据,包含通过与否、发现的问题描述、截图或选择器定位信息等:
export interface AuditFinding {
checklistItem: AuditChecklistItem;
status: 'pass' | 'fail' | 'not-applicable';
evidence?: {
selector?: string; // 问题元素的选择器
screenshot?: string; // 截图的引用地址
note?: string; // 补充说明
};
// 仅在 fail 时才需要填写修复建议,通过条件字段表达
remediation?: string;
}
export interface AuditReport {
projectName: string;
auditedAt: Date;
auditor: string;
findings: AuditFinding[];
wcagLevel: 'A' | 'AA'; // AODA 要求 AA
}这里的 remediation 用可选字段表达还不够严谨,因为一个通过的检查项不应该携带修复建议。这个问题可以通过可辨识联合解决,下一节详细展开。
三、用可辨识联合与泛型强化类型安全
可辨识联合(Discriminated Union)是 TypeScript 表达“状态与数据绑定”的利器。将 status 作为判别字段,可以让不同状态下允许携带的数据在类型层面严格区分:
export type AuditResult =
| {
status: 'pass';
checklistItem: AuditChecklistItem;
checkedBy: string;
}
| {
status: 'fail';
checklistItem: AuditChecklistItem;
evidence: {
selector: string; // 失败时必须提供定位
screenshot?: string;
};
remediation: string; // 失败时必须提供修复建议
remediationOwner: string;
}
| {
status: 'not-applicable';
checklistItem: AuditChecklistItem;
reason: string; // 需要说明不适用的原因
};这样建模之后,代码在使用 result 时必须先通过 switch (result.status) 进行类型收窄。编译器会保证:只有 status 为 fail 的结果才能访问 remediation 字段,而任何试图为通过项填写修复建议的代码都无法通过编译。这种约束使得非法状态不可表示,审查数据的质量在编译期就得到了保障。
再进一步,可以为报告统计定义泛型工具类型。例如一个根据严重级别过滤问题的工具函数,配合 TypeScript 内置的条件类型与映射类型,可以自动推断返回值结构:
// 从一批审查结果中提取指定严重级别的问题
export function filterBySeverity<T extends SeverityLevel>(
results: AuditResult[],
severity: T
): Extract<AuditResult, { status: 'fail' >[] {
return results.filter(
(r): r is Extract<AuditResult, { status: 'fail' > =>
r.status === 'fail' && r.checklistItem.severity === severity
);
}
// 计算报告是否符合 AODA 上线要求:critical 与 serious 问题必须为零
export function isAodaCompliant(report: AuditReport): boolean {
return report.findings.every(f => {
if (f.status !== 'fail') return true;
return f.checklistItem.severity !== 'critical'
&& f.checklistItem.severity !== 'serious';
});
}类型谓词 r is Extract<...> 让 filter 的返回值不再是宽泛的 AuditResult[],而是精确的失败结果数组,调用方无需再做额外的类型断言。这类细节在审查工具链规模扩大后尤为重要,能有效减少运行时的空值错误。
四、将类型体系集成到工程流程中
类型定义本身不产生价值,只有接入自动化流程才能发挥作用。实践中常见的做法有三种。第一种是配合 axe-core 或 Lighthouse 等自动检测工具,将其输出转换为上面定义的 AuditResult 结构存入版本库,作为每次回归的基线。第二种是在 CI 中运行审查脚本,调用 isAodaCompliant 判断是否存在 critical 级别问题,一旦存在就让流水线失败,从而阻断不合规版本的发布。第三种是搭建一个简单的内部页面,团队按 AuditChecklistItem 定义的人工检查项逐项勾选,提交的数据天然符合类型约束,汇总后自动生成报告。
维护层面需要注意规则的演进。WCAG 标准会更新,安大略省对 AODA 的执行要求也可能调整,因此 WcagRuleId 这类字面量联合类型建议与常量表一起集中管理,并附上标准出处注释。当规则变化时,修改一处类型定义即可让所有引用处获得编译器提示,快速定位需要同步更新的检查脚本和文档。
最后要强调的是,类型封装解决的是审查数据的规范与流程的自动化,它不能替代真实的残障用户测试与人工审查。对比度可以自动计算,但信息是否真的可理解、操作路径是否顺畅,仍需要结合屏幕阅读器实测和用户反馈。将 TypeScript 类型体系作为 AODA 合规工作的基础设施,配合持续的自动化检测与人工评估,才能构建出真正可访问、可持续维护的产品。
TypeScriptAODA无障碍设计审查修改时间:2026-08-31 04:08:47