From 46fc6ca41a1ce7603ffed2aa5b0b30ba787b1df0 Mon Sep 17 00:00:00 2001 From: Jack Franklin Date: Thu, 25 Jun 2020 13:38:01 +0100 Subject: [PATCH] feat(types): improve typing of `.evaluate()` (#6096) * feat(types): improve typing of `.evaluate()` This is the start of the work to take the types from the `@types/puppeteer` repository and port them into our repo so we can ship our built-in types out the box. This change types the `evaluate` function properly. It takes a generic type which is the type of the function you're passing, and the arguments and the return that you get back from the `evaluate` call are typed correctly. --- .eslintrc.js | 2 + README.md | 18 ++- new-docs/puppeteer.elementhandle.__eval.md | 6 +- new-docs/puppeteer.elementhandle._eval.md | 6 +- new-docs/puppeteer.evaluatefn.md | 12 ++ new-docs/puppeteer.evaluatefnreturntype.md | 12 ++ new-docs/puppeteer.frame.__eval.md | 6 +- new-docs/puppeteer.frame._eval.md | 6 +- new-docs/puppeteer.jshandle.evaluate.md | 8 +- new-docs/puppeteer.jsonarray.md | 12 ++ new-docs/puppeteer.jsonobject.md | 12 ++ new-docs/puppeteer.md | 6 + new-docs/puppeteer.page.__eval.md | 6 +- new-docs/puppeteer.page._eval.md | 6 +- new-docs/puppeteer.serializable.md | 12 ++ new-docs/puppeteer.serializableorjshandle.md | 12 ++ src/api-docs-entry.ts | 1 + src/common/DOMWorld.ts | 9 +- src/common/EvalTypes.ts | 58 ++++++++ src/common/FrameManager.ts | 9 +- src/common/JSHandle.ts | 132 ++++++++++--------- src/common/Page.ts | 9 +- 22 files changed, 264 insertions(+), 96 deletions(-) create mode 100644 new-docs/puppeteer.evaluatefn.md create mode 100644 new-docs/puppeteer.evaluatefnreturntype.md create mode 100644 new-docs/puppeteer.jsonarray.md create mode 100644 new-docs/puppeteer.jsonobject.md create mode 100644 new-docs/puppeteer.serializable.md create mode 100644 new-docs/puppeteer.serializableorjshandle.md create mode 100644 src/common/EvalTypes.ts diff --git a/.eslintrc.js b/.eslintrc.js index 3e40356283bde..32b5d16114941 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -96,6 +96,8 @@ module.exports = { "rules": { "no-unused-vars": 0, "@typescript-eslint/no-unused-vars": 2, + "func-call-spacing": 0, + "@typescript-eslint/func-call-spacing": 2, "semi": 0, "@typescript-eslint/semi": 2, "@typescript-eslint/no-empty-function": 0, diff --git a/README.md b/README.md index afbe88db88270..7bd432809f21d 100644 --- a/README.md +++ b/README.md @@ -292,9 +292,25 @@ Puppeteer creates its own browser user profile which it **cleans up on every run - debug your test inside chromium like a boss! - + + +## Usage with TypeScript + +We have recently completed a migration to move the Puppeteer source code from JavaScript to TypeScript and we're currently working on shipping type definitions for TypeScript with Puppeteer's npm package. + +Until this work is complete we recommend installing the Puppeteer type definitions from the [DefinitelyTyped](https://definitelytyped.org/) repository: + +```bash +npm install --save-dev @types/puppeteer +``` + +The types that you'll see appearing in the Puppeteer source code are based off the great work of those who have contributed to the `@types/puppeteer` package. We really appreciate the hard work those people put in to providing high quality TypeScript definitions for Puppeteer's users. + + + + ## Contributing to Puppeteer Check out [contributing guide](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md) to get an overview of Puppeteer development. diff --git a/new-docs/puppeteer.elementhandle.__eval.md b/new-docs/puppeteer.elementhandle.__eval.md index 62184b0bf0cd5..f351b5ef70c81 100644 --- a/new-docs/puppeteer.elementhandle.__eval.md +++ b/new-docs/puppeteer.elementhandle.__eval.md @@ -11,7 +11,7 @@ If `pageFunction` returns a Promise, then `frame.$$eval` would wait for the prom Signature: ```typescript -$$eval(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise; +$$eval(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise; ``` ## Parameters @@ -19,8 +19,8 @@ $$eval(selector: string, pageFunction: Function | string | Parameter | Type | Description | | --- | --- | --- | | selector | string | | -| pageFunction | Function \| string | | -| args | unknown\[\] | | +| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | | Returns: diff --git a/new-docs/puppeteer.elementhandle._eval.md b/new-docs/puppeteer.elementhandle._eval.md index 51719ae1bfd56..f0c8b8717eb96 100644 --- a/new-docs/puppeteer.elementhandle._eval.md +++ b/new-docs/puppeteer.elementhandle._eval.md @@ -11,7 +11,7 @@ If `pageFunction` returns a Promise, then `frame.$eval` would wait for the promi Signature: ```typescript -$eval(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise; +$eval(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise; ``` ## Parameters @@ -19,8 +19,8 @@ $eval(selector: string, pageFunction: Function | string, | Parameter | Type | Description | | --- | --- | --- | | selector | string | | -| pageFunction | Function \| string | | -| args | unknown\[\] | | +| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | | Returns: diff --git a/new-docs/puppeteer.evaluatefn.md b/new-docs/puppeteer.evaluatefn.md new file mode 100644 index 0000000000000..8647ef2b7d374 --- /dev/null +++ b/new-docs/puppeteer.evaluatefn.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [puppeteer](./puppeteer.md) > [EvaluateFn](./puppeteer.evaluatefn.md) + +## EvaluateFn type + + +Signature: + +```typescript +export declare type EvaluateFn = string | ((arg1: T, ...args: any[]) => any); +``` diff --git a/new-docs/puppeteer.evaluatefnreturntype.md b/new-docs/puppeteer.evaluatefnreturntype.md new file mode 100644 index 0000000000000..471823e316ee9 --- /dev/null +++ b/new-docs/puppeteer.evaluatefnreturntype.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [puppeteer](./puppeteer.md) > [EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md) + +## EvaluateFnReturnType type + + +Signature: + +```typescript +export declare type EvaluateFnReturnType = T extends (...args: any[]) => infer R ? R : unknown; +``` diff --git a/new-docs/puppeteer.frame.__eval.md b/new-docs/puppeteer.frame.__eval.md index 0a7d7a9e18d95..889af679f5cb0 100644 --- a/new-docs/puppeteer.frame.__eval.md +++ b/new-docs/puppeteer.frame.__eval.md @@ -7,7 +7,7 @@ Signature: ```typescript -$$eval(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise; +$$eval(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise; ``` ## Parameters @@ -15,8 +15,8 @@ $$eval(selector: string, pageFunction: Function | string | Parameter | Type | Description | | --- | --- | --- | | selector | string | | -| pageFunction | Function \| string | | -| args | unknown\[\] | | +| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | | Returns: diff --git a/new-docs/puppeteer.frame._eval.md b/new-docs/puppeteer.frame._eval.md index 0d5d3066c14d0..6eb63bae943f3 100644 --- a/new-docs/puppeteer.frame._eval.md +++ b/new-docs/puppeteer.frame._eval.md @@ -7,7 +7,7 @@ Signature: ```typescript -$eval(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise; +$eval(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise; ``` ## Parameters @@ -15,8 +15,8 @@ $eval(selector: string, pageFunction: Function | string, | Parameter | Type | Description | | --- | --- | --- | | selector | string | | -| pageFunction | Function \| string | | -| args | unknown\[\] | | +| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | | Returns: diff --git a/new-docs/puppeteer.jshandle.evaluate.md b/new-docs/puppeteer.jshandle.evaluate.md index fef1efe89fbd4..c8cd3227e228c 100644 --- a/new-docs/puppeteer.jshandle.evaluate.md +++ b/new-docs/puppeteer.jshandle.evaluate.md @@ -9,19 +9,19 @@ This method passes this handle as the first argument to `pageFunction`. Signature: ```typescript -evaluate(pageFunction: Function | string, ...args: unknown[]): Promise; +evaluate(pageFunction: T | string, ...args: SerializableOrJSHandle[]): Promise>; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| pageFunction | Function \| string | | -| args | unknown\[\] | | +| pageFunction | T \| string | | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | | Returns: -Promise<ReturnType> +Promise<[EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md)<T>> ## Example diff --git a/new-docs/puppeteer.jsonarray.md b/new-docs/puppeteer.jsonarray.md new file mode 100644 index 0000000000000..fea5db9a72110 --- /dev/null +++ b/new-docs/puppeteer.jsonarray.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [puppeteer](./puppeteer.md) > [JSONArray](./puppeteer.jsonarray.md) + +## JSONArray type + + +Signature: + +```typescript +export declare type JSONArray = Serializable[]; +``` diff --git a/new-docs/puppeteer.jsonobject.md b/new-docs/puppeteer.jsonobject.md new file mode 100644 index 0000000000000..609425b7804ef --- /dev/null +++ b/new-docs/puppeteer.jsonobject.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [puppeteer](./puppeteer.md) > [JSONObject](./puppeteer.jsonobject.md) + +## JSONObject interface + + +Signature: + +```typescript +export interface JSONObject +``` diff --git a/new-docs/puppeteer.md b/new-docs/puppeteer.md index 8b2c127a43936..200dab24ddb8e 100644 --- a/new-docs/puppeteer.md +++ b/new-docs/puppeteer.md @@ -52,6 +52,7 @@ | [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | | | [ClickOptions](./puppeteer.clickoptions.md) | | | [ConsoleMessageLocation](./puppeteer.consolemessagelocation.md) | | +| [JSONObject](./puppeteer.jsonobject.md) | | | [KeyDefinition](./puppeteer.keydefinition.md) | Copyright 2017 Google Inc. All rights reserved.Licensed under the Apache License, Version 2.0 (the 'License'); you may not use this file except in compliance with the License. You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. | | [Metrics](./puppeteer.metrics.md) | | | [PressOptions](./puppeteer.pressoptions.md) | | @@ -73,7 +74,12 @@ | Type Alias | Description | | --- | --- | | [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. | +| [EvaluateFn](./puppeteer.evaluatefn.md) | | +| [EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md) | | +| [JSONArray](./puppeteer.jsonarray.md) | | | [KeyInput](./puppeteer.keyinput.md) | | | [MouseButtonInput](./puppeteer.mousebuttoninput.md) | | | [PuppeteerErrors](./puppeteer.puppeteererrors.md) | | +| [Serializable](./puppeteer.serializable.md) | | +| [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md) | | diff --git a/new-docs/puppeteer.page.__eval.md b/new-docs/puppeteer.page.__eval.md index 4c7800a501902..3bcb6aea42873 100644 --- a/new-docs/puppeteer.page.__eval.md +++ b/new-docs/puppeteer.page.__eval.md @@ -7,7 +7,7 @@ Signature: ```typescript -$$eval(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise; +$$eval(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise; ``` ## Parameters @@ -15,8 +15,8 @@ $$eval(selector: string, pageFunction: Function | string | Parameter | Type | Description | | --- | --- | --- | | selector | string | | -| pageFunction | Function \| string | | -| args | unknown\[\] | | +| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | | Returns: diff --git a/new-docs/puppeteer.page._eval.md b/new-docs/puppeteer.page._eval.md index d5b18ad8e7794..8b1c1d7d1592c 100644 --- a/new-docs/puppeteer.page._eval.md +++ b/new-docs/puppeteer.page._eval.md @@ -7,7 +7,7 @@ Signature: ```typescript -$eval(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise; +$eval(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise; ``` ## Parameters @@ -15,8 +15,8 @@ $eval(selector: string, pageFunction: Function | string, | Parameter | Type | Description | | --- | --- | --- | | selector | string | | -| pageFunction | Function \| string | | -| args | unknown\[\] | | +| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | | Returns: diff --git a/new-docs/puppeteer.serializable.md b/new-docs/puppeteer.serializable.md new file mode 100644 index 0000000000000..8b7a7df8bf15b --- /dev/null +++ b/new-docs/puppeteer.serializable.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [puppeteer](./puppeteer.md) > [Serializable](./puppeteer.serializable.md) + +## Serializable type + + +Signature: + +```typescript +export declare type Serializable = number | string | boolean | null | JSONArray | JSONObject; +``` diff --git a/new-docs/puppeteer.serializableorjshandle.md b/new-docs/puppeteer.serializableorjshandle.md new file mode 100644 index 0000000000000..84ca6eed8e584 --- /dev/null +++ b/new-docs/puppeteer.serializableorjshandle.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [puppeteer](./puppeteer.md) > [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md) + +## SerializableOrJSHandle type + + +Signature: + +```typescript +export declare type SerializableOrJSHandle = Serializable | JSHandle; +``` diff --git a/src/api-docs-entry.ts b/src/api-docs-entry.ts index 8e8ac591378e1..4a04e31cfd683 100644 --- a/src/api-docs-entry.ts +++ b/src/api-docs-entry.ts @@ -48,3 +48,4 @@ export * from './common/Errors'; export * from './common/Tracing'; export * from './common/WebWorker'; export * from './common/USKeyboardLayout'; +export * from './common/EvalTypes'; diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts index 5600e557f1681..3ea66d50ccd6f 100644 --- a/src/common/DOMWorld.ts +++ b/src/common/DOMWorld.ts @@ -24,6 +24,7 @@ import { TimeoutSettings } from './TimeoutSettings'; import { MouseButtonInput } from './Input'; import { FrameManager, Frame } from './FrameManager'; import { getQueryHandlerAndSelector, QueryHandler } from './QueryHandler'; +import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes'; import { isNode } from '../environment'; // This predicateQueryHandler is declared here so that TypeScript knows about it @@ -155,8 +156,8 @@ export class DOMWorld { async $eval( selector: string, - pageFunction: Function | string, - ...args: unknown[] + pageFunction: EvaluateFn | string, + ...args: SerializableOrJSHandle[] ): Promise { const document = await this._document(); return document.$eval(selector, pageFunction, ...args); @@ -164,8 +165,8 @@ export class DOMWorld { async $$eval( selector: string, - pageFunction: Function | string, - ...args: unknown[] + pageFunction: EvaluateFn | string, + ...args: SerializableOrJSHandle[] ): Promise { const document = await this._document(); const value = await document.$$eval( diff --git a/src/common/EvalTypes.ts b/src/common/EvalTypes.ts new file mode 100644 index 0000000000000..c898d22be478e --- /dev/null +++ b/src/common/EvalTypes.ts @@ -0,0 +1,58 @@ +/** + * Copyright 2020 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { JSHandle } from './JSHandle'; + +/** + * @public + */ +export type EvaluateFn = string | ((arg1: T, ...args: any[]) => any); +/** + * @public + */ +export type EvaluateFnReturnType = T extends ( + ...args: any[] +) => infer R + ? R + : unknown; + +/** + * @public + */ +export type Serializable = + | number + | string + | boolean + | null + | JSONArray + | JSONObject; + +/** + * @public + */ +export type JSONArray = Serializable[]; + +/** + * @public + */ +export interface JSONObject { + [key: string]: Serializable; +} + +/** + * @public + */ +export type SerializableOrJSHandle = Serializable | JSHandle; diff --git a/src/common/FrameManager.ts b/src/common/FrameManager.ts index 6e56c75332952..859b649eaa92b 100644 --- a/src/common/FrameManager.ts +++ b/src/common/FrameManager.ts @@ -29,6 +29,7 @@ import { MouseButtonInput } from './Input'; import { Page } from './Page'; import { HTTPResponse } from './HTTPResponse'; import Protocol from '../protocol'; +import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes'; const UTILITY_WORLD_NAME = '__puppeteer_utility_world__'; @@ -454,16 +455,16 @@ export class Frame { async $eval( selector: string, - pageFunction: Function | string, - ...args: unknown[] + pageFunction: EvaluateFn | string, + ...args: SerializableOrJSHandle[] ): Promise { return this._mainWorld.$eval(selector, pageFunction, ...args); } async $$eval( selector: string, - pageFunction: Function | string, - ...args: unknown[] + pageFunction: EvaluateFn | string, + ...args: SerializableOrJSHandle[] ): Promise { return this._mainWorld.$$eval(selector, pageFunction, ...args); } diff --git a/src/common/JSHandle.ts b/src/common/JSHandle.ts index 4273db864cd90..2a8f0265b728b 100644 --- a/src/common/JSHandle.ts +++ b/src/common/JSHandle.ts @@ -23,6 +23,11 @@ import { KeyInput } from './USKeyboardLayout'; import { FrameManager, Frame } from './FrameManager'; import { getQueryHandlerAndSelector } from './QueryHandler'; import Protocol from '../protocol'; +import { + EvaluateFn, + SerializableOrJSHandle, + EvaluateFnReturnType, +} from './EvalTypes'; /** * @public @@ -113,11 +118,12 @@ export class JSHandle { * expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10'); * ``` */ - async evaluate( - pageFunction: Function | string, - ...args: unknown[] - ): Promise { - return await this.executionContext().evaluate( + + async evaluate( + pageFunction: T | string, + ...args: SerializableOrJSHandle[] + ): Promise> { + return await this.executionContext().evaluate>( pageFunction, this, ...args @@ -307,46 +313,48 @@ export class ElementHandle extends JSHandle { } private async _scrollIntoViewIfNeeded(): Promise { - const error = await this.evaluate>( - async (element: HTMLElement, pageJavascriptEnabled: boolean) => { - if (!element.isConnected) return 'Node is detached from document'; - if (element.nodeType !== Node.ELEMENT_NODE) - return 'Node is not of type HTMLElement'; - // force-scroll if page's javascript is disabled. - if (!pageJavascriptEnabled) { - element.scrollIntoView({ - block: 'center', - inline: 'center', - // Chrome still supports behavior: instant but it's not in the spec - // so TS shouts We don't want to make this breaking change in - // Puppeteer yet so we'll ignore the line. - // @ts-ignore - behavior: 'instant', - }); - return false; - } - const visibleRatio = await new Promise((resolve) => { - const observer = new IntersectionObserver((entries) => { - resolve(entries[0].intersectionRatio); - observer.disconnect(); - }); - observer.observe(element); + const error = await this.evaluate< + ( + element: HTMLElement, + pageJavascriptEnabled: boolean + ) => Promise + >(async (element, pageJavascriptEnabled) => { + if (!element.isConnected) return 'Node is detached from document'; + if (element.nodeType !== Node.ELEMENT_NODE) + return 'Node is not of type HTMLElement'; + // force-scroll if page's javascript is disabled. + if (!pageJavascriptEnabled) { + element.scrollIntoView({ + block: 'center', + inline: 'center', + // Chrome still supports behavior: instant but it's not in the spec + // so TS shouts We don't want to make this breaking change in + // Puppeteer yet so we'll ignore the line. + // @ts-ignore + behavior: 'instant', }); - if (visibleRatio !== 1.0) { - element.scrollIntoView({ - block: 'center', - inline: 'center', - // Chrome still supports behavior: instant but it's not in the spec - // so TS shouts We don't want to make this breaking change in - // Puppeteer yet so we'll ignore the line. - // @ts-ignore - behavior: 'instant', - }); - } return false; - }, - this._page.isJavaScriptEnabled() - ); + } + const visibleRatio = await new Promise((resolve) => { + const observer = new IntersectionObserver((entries) => { + resolve(entries[0].intersectionRatio); + observer.disconnect(); + }); + observer.observe(element); + }); + if (visibleRatio !== 1.0) { + element.scrollIntoView({ + block: 'center', + inline: 'center', + // Chrome still supports behavior: instant but it's not in the spec + // so TS shouts We don't want to make this breaking change in + // Puppeteer yet so we'll ignore the line. + // @ts-ignore + behavior: 'instant', + }); + } + return false; + }, this._page.isJavaScriptEnabled()); if (error) throw new Error(error); } @@ -491,9 +499,9 @@ export class ElementHandle extends JSHandle { * relative to the {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory} */ async uploadFile(...filePaths: string[]): Promise { - const isMultiple = await this.evaluate( - (element: HTMLInputElement) => element.multiple - ); + const isMultiple = await this.evaluate< + (element: HTMLInputElement) => boolean + >((element) => element.multiple); assert( filePaths.length <= 1 || isMultiple, 'Multiple file uploads only work with ' @@ -772,15 +780,15 @@ export class ElementHandle extends JSHandle { */ async $eval( selector: string, - pageFunction: Function | string, - ...args: unknown[] + pageFunction: EvaluateFn | string, + ...args: SerializableOrJSHandle[] ): Promise { const elementHandle = await this.$(selector); if (!elementHandle) throw new Error( `Error: failed to find element matching selector "${selector}"` ); - const result = await elementHandle.evaluate( + const result = await elementHandle.evaluate<(...args: any[]) => ReturnType>( pageFunction, ...args ); @@ -813,8 +821,8 @@ export class ElementHandle extends JSHandle { */ async $$eval( selector: string, - pageFunction: Function | string, - ...args: unknown[] + pageFunction: EvaluateFn | string, + ...args: SerializableOrJSHandle[] ): Promise { const defaultHandler = (element: Element, selector: string) => Array.from(element.querySelectorAll(selector)); @@ -827,7 +835,7 @@ export class ElementHandle extends JSHandle { queryHandler, updatedSelector ); - const result = await arrayHandle.evaluate( + const result = await arrayHandle.evaluate<(...args: any[]) => ReturnType>( pageFunction, ...args ); @@ -868,16 +876,18 @@ export class ElementHandle extends JSHandle { * Resolves to true if the element is visible in the current viewport. */ async isIntersectingViewport(): Promise { - return await this.evaluate>(async (element) => { - const visibleRatio = await new Promise((resolve) => { - const observer = new IntersectionObserver((entries) => { - resolve(entries[0].intersectionRatio); - observer.disconnect(); + return await this.evaluate<(element: Element) => Promise>( + async (element) => { + const visibleRatio = await new Promise((resolve) => { + const observer = new IntersectionObserver((entries) => { + resolve(entries[0].intersectionRatio); + observer.disconnect(); + }); + observer.observe(element); }); - observer.observe(element); - }); - return visibleRatio > 0; - }); + return visibleRatio > 0; + } + ); } } diff --git a/src/common/Page.ts b/src/common/Page.ts index ebe5bdced273b..0fd724fb022f1 100644 --- a/src/common/Page.ts +++ b/src/common/Page.ts @@ -42,6 +42,7 @@ import { FileChooser } from './FileChooser'; import { ConsoleMessage, ConsoleMessageType } from './ConsoleMessage'; import { PuppeteerLifeCycleEvent } from './LifecycleWatcher'; import Protocol from '../protocol'; +import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes'; const writeFileAsync = promisify(fs.writeFile); @@ -514,16 +515,16 @@ export class Page extends EventEmitter { async $eval( selector: string, - pageFunction: Function | string, - ...args: unknown[] + pageFunction: EvaluateFn | string, + ...args: SerializableOrJSHandle[] ): Promise { return this.mainFrame().$eval(selector, pageFunction, ...args); } async $$eval( selector: string, - pageFunction: Function | string, - ...args: unknown[] + pageFunction: EvaluateFn | string, + ...args: SerializableOrJSHandle[] ): Promise { return this.mainFrame().$$eval(selector, pageFunction, ...args); }