波兰网络无障碍指南把非文本内容当成一个独立审计域,而不是简单检查alt属性。IWAC在实现这些规则时,首先要解决类型定义问题。如果每个开发者都按自己的理解给图像、图标、音频、视频添加字段,运行时校验就会变成一坨难以维护的if。TypeScript的判别联合可以从编译期把不同媒体类型拆开,再配合穷尽检查保证每条规则都对应到具体分支。接下来从指南中的非文本分类谈起。

一、波兰网络无障碍指南对非文本内容的约束
指南要求审计工具能区分四类非文本元素:传达信息的图像、功能性图标、纯装饰元素、多媒体内容。它们在替代文本、隐藏标记、字幕和描述方面的要求完全不一样。举例来说,信息图像必须有非空alt或等效长描述;装饰元素反而不能给任何可被读屏识别的文本,需要aria-hidden或role=presentation;图标如果参与交互,就必须提供可访问名称,而不是alt。
如果用传统的interface NonTextContent { alt?: string }来表示,装饰元素和缺失字段就会混淆。空字符串alt在语义上是“故意装饰”,undefined却是“检查项缺失”。这两个状态在波兰指南中对应完全不同的处理逻辑。音频和视频更是没法用alt覆盖,它们需要transcript、captions、audioDescription等独立字段。
所以对IWAC来说,非文本内容不是一种类型,而是一组类型的集合。下面用一段对比说明:
- 信息图像:alt必填,允许longDescription。
- 装饰图像:alt必须为空字符串,ariaHidden为真。
- 功能性图标:无alt,但accessibleName必填。
- 音频:transcript必填,captions可选。
- 视频:transcript、captions必填,audioDescription可选。
这种分类如果不进类型系统,只能靠运行时写switch一类一变。但TS可以提前约束。下面给出类型建模。
二、用判别联合封装非文本内容类型
先定义基础接口,把公有的id、source、guidelineRef放进去。然后每个类型扩展基础接口,并用type字段标出唯一字面量。注意装饰图像的空字符串并不是“缺少值”,而是有效值。代码:
interface BaseNonTextContent {
id: string;
source: string;
/** 对应波兰网络无障碍指南条款编号 */
guidelineRef?: string;
}
interface InformativeImage extends BaseNonTextContent {
type: 'informative-image';
alt: string;
longDescription?: string;
}
interface DecorativeGraphic extends BaseNonTextContent {
type: 'decorative-graphic';
alt: '';
ariaHidden: true;
}
interface FunctionalIcon extends BaseNonTextContent {
type: 'functional-icon';
accessibleName: string;
iconRole: 'img' | 'button' | 'link';
}
interface AudioContent extends BaseNonTextContent {
type: 'audio';
transcript: string;
captions?: string[];
}
interface VideoContent extends BaseNonTextContent {
type: 'video';
transcript: string;
captions: string[];
audioDescription?: string;
signLanguage?: boolean;
}上面代码中type是判别字段,InformativeImage等必须通过类型守卫才能访问各自独有字段。接着将五个接口合成联合类型:
type NonTextContent = | InformativeImage | DecorativeGraphic | FunctionalIcon | AudioContent | VideoContent;
联合类型的好处是,当你拿到NonTextContent后访问content.alt,TS会报错,因为不是所有成员都有alt。这迫使你在访问前先收窄类型。与用可选字段相比,这种设计把“哪些类型有alt、哪些必须用accessibleName”都编码进了编译期。坏处是类型声明变长,新增媒体类型时需要同步更新联合、校验函数和映射函数。
三、类型守卫与穷尽检查驱动规则落地
类型定义只是第一步,真正执行波兰指南规则要靠类型守卫和穷尽检查。写一个校验函数,在switch中按type分支检查必填字段是否有效。对于装饰图形,需要同时满足alt为空和ariaHidden为真,这里用逻辑与需转义。代码:
function assertNonTextAccessibility(content: NonTextContent): boolean {
switch (content.type) {
case 'informative-image':
return content.alt.trim().length !== 0;
case 'decorative-graphic':
return content.alt === '' && content.ariaHidden === true;
case 'functional-icon':
return content.accessibleName.trim().length !== 0;
case 'audio':
return content.transcript.trim().length !== 0;
case 'video':
return content.transcript.trim().length !== 0 && content.captions.length !== 0;
default: {
const exhaustive: never = content;
throw new Error('未处理的非文本类型: ' + exhaustive);
}
}
}default分支用never做穷尽检查。以后如果往NonTextContent里加了CanvasChart或DataTable类型,却没有在switch中处理,编译会直接报错。这比运行时发现漏检强得多。
再补充一个类型守卫,用于从unknown输入中提取非文本内容。实际IWAC从JSON或DOM解析出来的数据都是unknown,不能直接假定为NonTextContent。需要先检查基础形状。代码:
function isDecorative(content: NonTextContent): content is DecorativeGraphic {
return content.type === 'decorative-graphic';
}
function parseNonTextContent(raw: unknown): NonTextContent | null {
if (typeof raw !== 'object' || raw === null) return null;
const candidate = raw as { type?: unknown };
if (typeof candidate.type !== 'string') return null;
return candidate as NonTextContent;
}isDecorative返回类型谓词,调用方在if块内拿到的就是DecorativeGraphic,可以安全访问ariaHidden。不建议用as强制断言,因为一旦来源数据格式变化,强转会掩盖错误。
四、ARIA映射与模块化组织
非文本内容最终要渲染到DOM或生成审计报告,需要映射为aria-label、aria-hidden、role、alt等属性。可以定义一个A11yProps接口,并让toA11yProps统一处理。这样渲染层不关心具体类型,只消费生成好的属性。代码:
interface A11yProps {
'aria-label'?: string;
'aria-hidden'?: boolean;
role?: string;
alt?: string;
}
function toA11yProps(content: NonTextContent): A11yProps {
switch (content.type) {
case 'informative-image':
return { alt: content.alt, role: 'img' };
case 'decorative-graphic':
return { alt: '', 'aria-hidden': true, role: 'presentation' };
case 'functional-icon':
return { 'aria-label': content.accessibleName, role: content.iconRole };
case 'audio':
return { 'aria-label': '音频内容' };
case 'video':
return { 'aria-label': '视频内容' };
default: {
const exhaustive: never = content;
return exhaustive;
}
}
}在IWAC仓库中,建议把类型定义放在types/media.ts,类型守卫放在guards/media.ts,ARIA映射放在mappers/a11y.ts。不要把全部类型塞进单个index.ts,否则后续加类型时会互相影响。
类型定义和指南版本要保持同步。可以在注释里写明对应的波兰指南条款编号,这样每次指南更新时,开发者能快速定位需要修改的接口。这套体系的最终目标不是单纯通过类型检查,而是让无障碍规则从口口相传变成编译器可验证的契约。
以上从指南分类、类型建模、校验收窄到ARIA映射,用TypeScript为IWAC封装非文本内容类型定义。这样审计模块不再靠散落的字符串判断,每条非文本规则都落到具体联合成员上。
TypeScriptIWAC非文本内容类型修改时间:2026-09-25 15:27:03