最近,我想在我的应用程序中使用一个
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
你的环境类型声明文件通过添加到types
或typeRoots
在tsconfig.json
编译器选项中被识别为这样的文件
在你的环境声明文件的末尾添加export {}
,这样TypeScript就会将这个文件识别为一个模块