导读:本期聚焦于半糖创作的《TypeScript如何定义Screen Capture API屏幕捕获的视频约束类型?》,敬请观看详情。调用codegetDisplayMedia/code实现屏幕录制时,如何用TypeScript准确描述视频约束对象是不少前端工程师遇到的难题。Screen Capture API返回的MediaTrackConstraints包含宽度、高度、帧率、宽高比、displaySurface、cursor等一系列字段,直接使用any会失去类型检查的价值,手写接口又容易遗漏或写错字段名。本文从浏览器原生类型入手,先讲解MediaTrackConstraintSet与自定义约束的关系,再针对屏幕捕获特有的displaySurface、logicalSurface、cursor等属性定义完整接口,最后结合Elevated permissions和监视器采集等进阶场景给出类型收窄方案与实用工具类型写法,帮助你在编译阶段就捕获约束配置错误。

在使用 Screen Capture API 也就是 getDisplayMedia 做屏幕共享或录制功能时,前端通常需要传入一个包含视频约束的对象。很多同学在 TypeScript 项目里直接写了 { video: true },一旦想配置分辨率、帧率、显示器类型这些细节,就开始犯难:这些字段到底该怎么声明类型?直接用 any 固然省事,但编译期检查就形同虚设。这篇文章就来把屏幕捕获场景下视频约束的数据类型定义彻底讲清楚。

TypeScript如何定义Screen Capture API屏幕捕获的视频约束类型?

一、先弄清楚原生类型体系

TypeScript 的 DOM 库(lib.dom.d.ts)其实已经内置了与媒体捕获相关的类型。getDisplayMedia 挂在 MediaDevices 原型上,签名大致是 getDisplayMedia(options?: DisplayMediaStreamOptions): Promise<MediaStream>。而 DisplayMediaStreamOptions 里的 video 字段并不是普通的 boolean,它可以是布尔值,也可以是 MediaTrackConstraints

MediaTrackConstraints 继承自 MediaTrackConstraintSet,后者包含了宽度、高度、帧率、宽高比等所有可约束属性。理解这个继承关系很重要:你自定义的约束类型如果 extends 了这些原生接口,就能同时获得摄像头和屏幕捕获共有的字段,再补充屏幕捕获特有的字段即可。下面是一个基础的自定义类型:

// 屏幕捕获专有的约束字段
interface ScreenCaptureConstraintSet {
  displaySurface?: 'monitor' | 'window' | 'browser';
  logicalSurface?: boolean;
  cursor?: 'always' | 'motion' | 'never';
  restrictOwnAudio?: boolean;
}

// 组合通用约束与屏幕捕获专有约束
type ScreenVideoConstraints = MediaTrackConstraintSet & ScreenCaptureConstraintSet;

interface ScreenCaptureOptions {
  video: boolean | ScreenVideoConstraints;
  audio?: boolean | MediaTrackConstraints;
  // 是否请求系统级权限( Elevated permissions )
  systemAudio?: 'include' | 'exclude';
  surfaceSwitching?: 'include' | 'exclude';
  selfBrowserSurface?: 'include' | 'exclude';
}

这样定义的好处是显而易见的:宽度、帧率这类字段直接复用官方类型,ConstrainULong 既允许传数字也允许传 { ideal: 1920 } 这种形式,不需要你自己重复描述。而 displaySurface 这类屏幕捕获独有字段则用字面量联合类型约束,写错取值编译器立刻报错。

二、约束值的进阶写法与类型收窄

原生类型里每个约束字段的值都是宽松的联合类型,比如 width?: ConstrainULong,展开后等于 number | ConstrainULongRange。实际业务中我们往往只需要 { ideal?: number; max?: number; min?: number } 这几种形态。可以通过工具类型做一层收窄,让调用方传参更受控,同时保留灵活性。

// 只保留 ideal / min / max 的范围约束
type NarrowULong = number | { ideal?: number; min?: number; max?: number };

type NarrowConstraintSet = {
  width?: NarrowULong;
  height?: NarrowULong;
  frameRate?: NarrowULong;
  aspectRatio?: NarrowULong;
  displaySurface?: 'monitor' | 'window' | 'browser';
  cursor?: 'always' | 'motion' | 'never';
};

async function captureScreen(video: NarrowConstraintSet) {
  const stream = await navigator.mediaDevices.getDisplayMedia({
    video: video as MediaTrackConstraints,
    audio: false,
  });
  return stream;
}

captureScreen({ width: { ideal: 1920 }, frameRate: 15, displaySurface: 'monitor' });

注意这里的 as MediaTrackConstraints 断言。因为我们收窄后的类型是原生类型的子集,理论上可以直接赋值,但如果把字段写成了可选的 exactadvanced 等扩展项,就可能需要断言兜底。断言要谨慎使用,仅在你确认字段取值范围合法时才加上,否则等于亲手关掉了类型检查。

另一个常见需求是区分“预置配置”。比如产品里可能提供“高清录制”“流畅共享”两种模式,用联合类型配合判别字段可以让函数签名更清晰:

type CaptureMode =
  | { mode: 'hd'; constraints: { width: { ideal: 1920 }; height: { ideal: 1080 }; frameRate: 30 } }
  | { mode: 'smooth'; constraints: { frameRate: { ideal: 60 }; displaySurface: 'monitor' } };

function startCapture(config: CaptureMode) {
  return navigator.mediaDevices.getDisplayMedia({ video: config.constraints, audio: false });
}

三、运行时校验与类型定义的配合

再完善的静态类型也拦不住运行时数据,比如约束配置来自后端接口或用户在界面上勾选。这时候类型定义只保证编译期正确,真正传给浏览器前还应该做一次字段白名单过滤。白名单的意义在于,浏览器对未知字段不报错但也不会生效,而过滤掉非法字段能避免误导排查方向。

const ALLOWED_KEYS = [
  'width', 'height', 'frameRate', 'aspectRatio',
  'displaySurface', 'logicalSurface', 'cursor',
] as const;

type AllowedKey = typeof ALLOWED_KEYS[number];

function sanitizeConstraints(input: Record<string, unknown>): ScreenVideoConstraints {
  const result: Record<AllowedKey, unknown> = {} as Record<AllowedKey, unknown>;
  for (const key of ALLOWED_KEYS) {
    if (key in input) {
      result[key] = input[key];
    }
  }
  return result as ScreenVideoConstraints;
}

// 后端下发的配置先过滤再使用
const remoteConfig = { width: { ideal: 2560 }, frameRate: 30, evilField: 'x' };
const safe = sanitizeConstraints(remoteConfig);
navigator.mediaDevices.getDisplayMedia({ video: safe, audio: false });

这套方案里,静态类型负责开发阶段的提示与约束,白名单过滤负责运行阶段的兜底,两层防护结合起来才算完整。还可以进一步用 zod 这类校验库替代手写白名单,把 schema 推导出的类型直接赋给约束对象,实现一处定义、编译期和运行时共用。

最后提醒一点,屏幕捕获的约束语义和摄像头不同:displaySurface 只是给浏览器的提示,用户在选共享源时仍可改选;widthheight 也受共享源实际分辨率限制,通常配合 applyConstraints 在拿到轨道后再二次调整。理解了这些行为差异,类型定义才能真正贴合实际运行效果,而不是停留在纸面上。

TypeScriptScreen Capture API视频约束修改时间:2026-09-04 17:42:41

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260904/50383.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。