相关文章推荐
含蓄的火锅  ·  php - curl ...·  1 年前    · 
神勇威武的跑步机  ·  ORA-28579:network ...·  1 年前    · 
被表白的大葱  ·  How to - Group, Sort ...·  1 年前    · 

最近,我想在我的应用程序中使用一个 ResizeObserver ResizeObserver ,但当你在TypeScript中使用它时--在写这篇文章时-- ResizeObserver 不会被识别为一个有效的对象(或构造函数)。那么这是为什么呢?

DOM API类型如何在TypeScript中落地 #

TypeScript将所有DOM APIs的类型存储在 lib.dom.d.ts 。这个文件是由 Web IDL 文件自动生成的。 Web IDL Web界面定义语言 的缩写,是W3C和WHATWG用来定义Web APIs接口的一种格式。它在2012年左右问世,自2016年起成为一种标准。

当你在 W3C 阅读标准时--你可以在规范的某个地方看到部分定义或完整的定义。比如这个。

enum ResizeObserverBoxOptions {
  "border-box", "content-box", "device-pixel-content-box"
dictionary ResizeObserverOptions {
  ResizeObserverBoxOptions box = "content-box";
[Exposed=(Window)]
interface ResizeObserver {
  constructor(ResizeObserverCallback callback);
  void observe(Element target, optional ResizeObserverOptions options);
  void unobserve(Element target);
  void disconnect();
callback ResizeObserverCallback = void (sequence<ResizeObserverEntry> entries, ResizeObserver observer);
[Exposed=Window]
interface ResizeObserverEntry {
  readonly attribute Element target;
  readonly attribute DOMRectReadOnly contentRect;
  readonly attribute FrozenArray<ResizeObserverSize> borderBoxSize;
  readonly attribute FrozenArray<ResizeObserverSize> contentBoxSize;
  readonly attribute FrozenArray<ResizeObserverSize> devicePixelContentBoxSize;
interface ResizeObserverSize {
  readonly attribute unrestricted double inlineSize;
  readonly attribute unrestricted double blockSize;
interface ResizeObservation {
  constructor(Element target);
  readonly attribute Element target;
  readonly attribute ResizeObserverBoxOptions observedBox;
  readonly attribute FrozenArray<ResizeObserverSize> lastReportedSizes;

浏览器使用这个作为准则来实现各自的API。TypeScript使用这些IDL文件来生成lib.dom.d.ts 。该 TS JS Lib生成器项目搜刮网络标准并提取IDL信息。然后,一个IDL到TypeScript的解析器会生成正确的类型。

刮削的页面是手动维护的。当一个规范足够远,并被所有主要浏览器支持的时候,人们就会添加一个新的资源,并看到他们的变化随着即将到来的TypeScript版本发布。lib.dom.d.ts因此,在ResizeObserver ,这只是一个时间问题。

如果我们不能等待,我们就可以自己添加类型。而且只针对我们目前工作的项目。

周围的声明#

让我们假设我们为ResizeObserver 生成了类型。我们会将输出的内容存储在一个叫做resize-observer.d.ts 的文件中。以下是其内容。

type ResizeObserverBoxOptions =
  "border-box" | 
  "content-box" |
  "device-pixel-content-box";
interface ResizeObserverOptions {
  box?: ResizeObserverBoxOptions;
interface ResizeObservation {
  readonly lastReportedSizes: ReadonlyArray<ResizeObserverSize>;
  readonly observedBox: ResizeObserverBoxOptions;
  readonly target: Element;
declare var ResizeObservation: {
  prototype: ResizeObservation;
  new(target: Element): ResizeObservation;
interface ResizeObserver {
  disconnect(): void;
  observe(target: Element, options?: ResizeObserverOptions): void;
  unobserve(target: Element): void;
export declare var ResizeObserver: {
  prototype: ResizeObserver;
  new(callback: ResizeObserverCallback): ResizeObserver;
interface ResizeObserverEntry {
  readonly borderBoxSize: ReadonlyArray<ResizeObserverSize>;
  readonly contentBoxSize: ReadonlyArray<ResizeObserverSize>;
  readonly contentRect: DOMRectReadOnly;
  readonly devicePixelContentBoxSize: ReadonlyArray<ResizeObserverSize>;
  readonly target: Element;
declare var ResizeObserverEntry: {
  prototype: ResizeObserverEntry;
  new(): ResizeObserverEntry;
interface ResizeObserverSize {
  readonly blockSize: number;
  readonly inlineSize: number;
declare var ResizeObserverSize: {
  prototype: ResizeObserverSize;
  new(): ResizeObserverSize;
interface ResizeObserverCallback {
  (entries: ResizeObserverEntry[], observer: ResizeObserver): void;

我们声明了大量的接口,以及一些实现我们接口的变量,比如declare var ResizeObserver ,它是定义原型和构造函数的对象。

declare var ResizeObserver: {
  prototype: ResizeObserver;
  new(callback: ResizeObserverCallback): ResizeObserver;

这已经有了很大的帮助。我们可以使用--可以说是--长的类型声明,并把它们直接放在我们需要的文件中。ResizeObserver 找到了!不过我们想让它到处都能用。

增强全局#的作用

感谢TypeScript的声明合并功能,我们可以根据需要扩展命名空间接口。这里有几篇关于如何扩展Object JJSX类型等的文章。这一次,我们要扩展全局命名空间

全局命名空间包含所有的对象和接口,嗯,全局可用。像窗口对象(和Window 接口),以及其他一切应该是我们的JavaScript执行环境的一部分的地方。我们增强全局命名空间,并将ResizeObserver 对象加入其中。

declare global { // opening up the namespace
  var ResizeObserver: { // mergin ResizeObserver to it
    prototype: ResizeObserver;
    new(callback: ResizeObserverCallback): ResizeObserver;

让我们把resize-observer.d.ts 放在一个叫做@types 的文件夹里。不要忘了把这个文件夹添加到TypeScript应该解析的源中,以及TypeScript的类型声明文件夹列表中。tsconfig.json

"compilerOptions": { //... "typeRoots": ["@types", "./node_modules/@types"], //... "include": ["src", "@types"]

因为可能有很大的可能性,ResizeObserver 在你的目标浏览器中还不可用,确保你使ResizeObserver 对象可能是undefined 。这敦促你检查该对象是否可用。

declare global {
  var ResizeObserver: {
    prototype: ResizeObserver;
    new(callback: ResizeObserverCallback): ResizeObserver;
  } | undefined

在你的应用程序中。

if(typeof ResizeObserver !== 'undefined') {
  const x = new ResizeObserver((entries) => {})

这使得使用ResizeObserver 尽可能的安全!

故障排除#

可能是TypeScript没有接收到你的环境声明文件和全局增强。如果发生这种情况,请确保。

  • 你通过include 中的属性解析了@types 文件夹。tsconfig.json
  • 你的环境类型声明文件通过添加到typestypeRootstsconfig.json 编译器选项中被识别为这样的文件
  • 在你的环境声明文件的末尾添加export {} ,这样TypeScript就会将这个文件识别为一个模块
  • 1.2W字 | 了不起的 TypeScript 入门教程
  • Vue3.0 前的 TypeScript 最佳入门实践
  • ts保姆级教程,别再说你不会ts了
  •