TypeScript 与无障碍:如何编写人人可用的代码

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么无障碍不是“打勾”,而是关乎人

老实说:当我们听到“无障碍”(或 a11y)时,很多人会想到无尽的需求清单、枯燥的检查,以及“嗯,这是给有障碍的人用的”这种说法。但事实是,可访问性关乎每一个人。你是否曾尝试单手拿着手机在路上订披萨?或者在地铁里网络不好,网站加载半天?这些就是可访问性问题。

在本文中,我们将讨论TypeScript 如何成为您创建可访问 Web 应用程序的最佳伙伴。不仅仅是“添加标签”,而是构建一种架构,让包容性不是一项功能,而是基础。



TypeScript 作为 a11y 错误的盾牌

许多人认为 TypeScript 只关乎数据类型和防止错误。但实际上,强类型在可访问性方面也能提供保护。让我们通过示例来了解。



ARIA 属性的类型化

最常见的问题之一是 ARIA 属性值错误。您是否曾在 div 上写过 role="button"?然后忘了添加 tabindex?TypeScript 可以在编译阶段捕获这些问题。

// 不好:没有保护
function createButton(label: string) {
  return `<div role="button">${label}</div>`;
}

// 好:对 ARIA 进行类型化
type AriaRole = 'button' | 'navigation' | 'alert' | 'dialog';

interface AriaAttributes {
  role: AriaRole;
  'aria-label'?: string;
  'aria-expanded'?: boolean;
  tabIndex?: number;
}

function createAccessibleButton(label: string, aria: AriaAttributes) {
  // 现在 TypeScript 会检查角色是否有效
  return {
    tag: 'div',
    attrs: aria,
    content: label
  };
}

// 错误!'btn' 不属于 AriaRole 类型
// createAccessibleButton('点击我', { role: 'btn' });

感受到区别了吗?TypeScript 不会让您意外写出 role="btutton" 或忘记必需属性。这就像一位私人助理在低语:“嘿,你确定这是可访问的吗?”



焦点状态的严格检查

另一个痛点——焦点管理。尤其是在 SPA 中,页面不会重新加载。TypeScript 有助于创建安全包装器。

// 定义哪些元素可以获取焦点
type FocusableElement = HTMLInputElement | HTMLButtonElement | HTMLAnchorElement | HTMLSelectElement | HTMLTextAreaElement;

function safelySetFocus(element: FocusableElement | null): void {
  if (element && document.contains(element)) {
    element.focus();
  } else {
    console.warn('元素不在 DOM 中或无法获取焦点');
  }
}

// 使用
const modal = document.querySelector('.modal__close-btn');
safelySetFocus(modal as FocusableElement | null);

请注意:我们不只是调用 .focus(),而是检查元素是否存在以及是否可以被聚焦。这可以防止那些可能让屏幕阅读器用户陷入空白的错误。



构建以 a11y 为核心的架构

现在让我们进入更高级的内容。如何让可访问性嵌入到项目的架构中,而不是在发布前临时拼凑?



组件的类型化属性

为所有组件创建一个基础接口。这就像可访问性的宪法。

// 所有 UI 组件的基础接口
interface AccessibleComponentProps {
  id?: string;
  className?: string;
  ariaLabel?: string;
  ariaDescribedby?: string;
  role?: string;
  tabIndex?: number;
  isDisabled?: boolean;
  onClick?: () => void;
  onKeyDown?: (e: React.KeyboardEvent) => void;
}

// 具体的按钮组件
interface ButtonProps extends AccessibleComponentProps {
  variant                

博客

书籍推荐