Skip to content

Commit 5049b83

Browse files
authoredJul 3, 2020
feat(types): add types for page.$$eval (#6139)
* feat(types): add types for `page.$$eval` * Add new-docs for $$eval * fix example * linting
1 parent f7857d2 commit 5049b83

8 files changed

+119
-46
lines changed
 

Diff for: ‎new-docs/puppeteer.elementhandle.__eval.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,20 @@ If `pageFunction` returns a Promise, then `frame.$$eval` would wait for the prom
1111
<b>Signature:</b>
1212

1313
```typescript
14-
$$eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
14+
$$eval<ReturnType>(selector: string, pageFunction: (elements: Element[], ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
1515
```
1616

1717
## Parameters
1818

1919
| Parameter | Type | Description |
2020
| --- | --- | --- |
2121
| selector | string | |
22-
| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | |
22+
| pageFunction | (elements: Element\[\], ...args: unknown\[\]) =&gt; ReturnType \| Promise&lt;ReturnType&gt; | |
2323
| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
2424

2525
<b>Returns:</b>
2626

27-
Promise&lt;ReturnType&gt;
27+
Promise&lt;[WrapElementHandle](./puppeteer.wrapelementhandle.md)<!-- -->&lt;ReturnType&gt;&gt;
2828

2929
## Example 1
3030

Diff for: ‎new-docs/puppeteer.frame.__eval.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@
77
<b>Signature:</b>
88

99
```typescript
10-
$$eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
10+
$$eval<ReturnType>(selector: string, pageFunction: (elements: Element[], ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
1111
```
1212

1313
## Parameters
1414

1515
| Parameter | Type | Description |
1616
| --- | --- | --- |
1717
| selector | string | |
18-
| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | |
18+
| pageFunction | (elements: Element\[\], ...args: unknown\[\]) =&gt; ReturnType \| Promise&lt;ReturnType&gt; | |
1919
| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
2020

2121
<b>Returns:</b>
2222

23-
Promise&lt;ReturnType&gt;
23+
Promise&lt;[WrapElementHandle](./puppeteer.wrapelementhandle.md)<!-- -->&lt;ReturnType&gt;&gt;
2424

Diff for: ‎new-docs/puppeteer.page.__eval.md

+31-8
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,22 @@ This method runs `Array.from(document.querySelectorAll(selector))` within the pa
99
<b>Signature:</b>
1010

1111
```typescript
12-
$$eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
12+
$$eval<ReturnType>(selector: string, pageFunction: (elements: Element[], ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
1313
```
1414

1515
## Parameters
1616

1717
| Parameter | Type | Description |
1818
| --- | --- | --- |
1919
| selector | string | the [selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) to query for |
20-
| pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | the function to be evaluated in the page context. Will be passed the result of <code>Array.from(document.querySelectorAll(selector))</code> as its first argument. |
20+
| pageFunction | (elements: Element\[\], ...args: unknown\[\]) =&gt; ReturnType \| Promise&lt;ReturnType&gt; | the function to be evaluated in the page context. Will be passed the result of <code>Array.from(document.querySelectorAll(selector))</code> as its first argument. |
2121
| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | any additional arguments to pass through to <code>pageFunction</code>. |
2222

2323
<b>Returns:</b>
2424

25-
Promise&lt;ReturnType&gt;
25+
Promise&lt;[WrapElementHandle](./puppeteer.wrapelementhandle.md)<!-- -->&lt;ReturnType&gt;&gt;
2626

27-
The result of calling `pageFunction`<!-- -->.
27+
The result of calling `pageFunction`<!-- -->. If it returns an element it is wrapped in an [ElementHandle](./puppeteer.elementhandle.md)<!-- -->, else the raw value itself is returned.
2828

2929
## Remarks
3030

@@ -33,17 +33,40 @@ If `pageFunction` returns a promise `$$eval` will wait for the promise to resolv
3333
## Example 1
3434

3535

36-
```js
36+
```
37+
// get the amount of divs on the page
3738
const divCount = await page.$$eval('div', divs => divs.length);
3839
40+
// get the text content of all the `.options` elements:
41+
const options = await page.$$eval('div > span.options', options => {
42+
return options.map(option => option.textContent)
43+
});
44+
3945
```
46+
If you are using TypeScript, you may have to provide an explicit type to the first argument of the `pageFunction`<!-- -->. By default it is typed as `Element[]`<!-- -->, but you may need to provide a more specific sub-type:
4047

4148
## Example 2
4249

4350

44-
```js
45-
const options = await page.$$eval(
46-
'div > span.options', options => options.map(option => option.textContent));
51+
```
52+
// if you don't provide HTMLInputElement here, TS will error
53+
// as `value` is not on `Element`
54+
await page.$$eval('input', (elements: HTMLInputElement[]) => {
55+
return elements.map(e => e.value);
56+
});
57+
58+
```
59+
The compiler should be able to infer the return type from the `pageFunction` you provide. If it is unable to, you can use the generic type to tell the compiler what return type you expect from `$$eval`<!-- -->:
60+
61+
## Example 3
62+
63+
64+
```
65+
// The compiler can infer the return type in this case, but if it can't
66+
// or if you want to be more explicit, provide it as the generic type.
67+
const allInputValues = await page.$$eval<string[]>(
68+
'input', (elements: HTMLInputElement[]) => elements.map(e => e.textContent)
69+
);
4770
4871
```
4972

Diff for: ‎src/common/DOMWorld.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import { MouseButton } from './Input';
2525
import { FrameManager, Frame } from './FrameManager';
2626
import { getQueryHandlerAndSelector, QueryHandler } from './QueryHandler';
2727
import {
28-
EvaluateFn,
2928
SerializableOrJSHandle,
3029
EvaluateHandleFn,
3130
WrapElementHandle,
@@ -172,11 +171,14 @@ export class DOMWorld {
172171
return document.$eval<ReturnType>(selector, pageFunction, ...args);
173172
}
174173

175-
async $$eval<ReturnType extends any>(
174+
async $$eval<ReturnType>(
176175
selector: string,
177-
pageFunction: EvaluateFn | string,
176+
pageFunction: (
177+
elements: Element[],
178+
...args: unknown[]
179+
) => ReturnType | Promise<ReturnType>,
178180
...args: SerializableOrJSHandle[]
179-
): Promise<ReturnType> {
181+
): Promise<WrapElementHandle<ReturnType>> {
180182
const document = await this._document();
181183
const value = await document.$$eval<ReturnType>(
182184
selector,

Diff for: ‎src/common/FrameManager.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import { Page } from './Page';
3030
import { HTTPResponse } from './HTTPResponse';
3131
import Protocol from '../protocol';
3232
import {
33-
EvaluateFn,
3433
SerializableOrJSHandle,
3534
EvaluateHandleFn,
3635
WrapElementHandle,
@@ -496,11 +495,14 @@ export class Frame {
496495
return this._mainWorld.$eval<ReturnType>(selector, pageFunction, ...args);
497496
}
498497

499-
async $$eval<ReturnType extends any>(
498+
async $$eval<ReturnType>(
500499
selector: string,
501-
pageFunction: EvaluateFn | string,
500+
pageFunction: (
501+
elements: Element[],
502+
...args: unknown[]
503+
) => ReturnType | Promise<ReturnType>,
502504
...args: SerializableOrJSHandle[]
503-
): Promise<ReturnType> {
505+
): Promise<WrapElementHandle<ReturnType>> {
504506
return this._mainWorld.$$eval<ReturnType>(selector, pageFunction, ...args);
505507
}
506508

Diff for: ‎src/common/JSHandle.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -882,11 +882,14 @@ export class ElementHandle<
882882
* .toEqual(['Hello!', 'Hi!']);
883883
* ```
884884
*/
885-
async $$eval<ReturnType extends any>(
885+
async $$eval<ReturnType>(
886886
selector: string,
887-
pageFunction: EvaluateFn | string,
887+
pageFunction: (
888+
elements: Element[],
889+
...args: unknown[]
890+
) => ReturnType | Promise<ReturnType>,
888891
...args: SerializableOrJSHandle[]
889-
): Promise<ReturnType> {
892+
): Promise<WrapElementHandle<ReturnType>> {
890893
const defaultHandler = (element: Element, selector: string) =>
891894
Array.from(element.querySelectorAll(selector));
892895
const { updatedSelector, queryHandler } = getQueryHandlerAndSelector(
@@ -898,12 +901,17 @@ export class ElementHandle<
898901
queryHandler,
899902
updatedSelector
900903
);
901-
const result = await arrayHandle.evaluate<(...args: any[]) => ReturnType>(
902-
pageFunction,
903-
...args
904-
);
904+
const result = await arrayHandle.evaluate<
905+
(
906+
elements: Element[],
907+
...args: unknown[]
908+
) => ReturnType | Promise<ReturnType>
909+
>(pageFunction, ...args);
905910
await arrayHandle.dispose();
906-
return result;
911+
/* This as exists for the same reason as the `as` in $eval above.
912+
* See the comment there for a ful explanation.
913+
*/
914+
return result as WrapElementHandle<ReturnType>;
907915
}
908916

909917
/**

Diff for: ‎src/common/Page.ts

+52-14
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import { ConsoleMessage, ConsoleMessageType } from './ConsoleMessage';
4343
import { PuppeteerLifeCycleEvent } from './LifecycleWatcher';
4444
import Protocol from '../protocol';
4545
import {
46-
EvaluateFn,
4746
SerializableOrJSHandle,
4847
EvaluateHandleFn,
4948
WrapElementHandle,
@@ -795,31 +794,70 @@ export class Page extends EventEmitter {
795794
* resolve and then return its value.
796795
*
797796
* @example
798-
* ```js
797+
*
798+
* ```
799+
* // get the amount of divs on the page
799800
* const divCount = await page.$$eval('div', divs => divs.length);
801+
*
802+
* // get the text content of all the `.options` elements:
803+
* const options = await page.$$eval('div > span.options', options => {
804+
* return options.map(option => option.textContent)
805+
* });
800806
* ```
801807
*
808+
* If you are using TypeScript, you may have to provide an explicit type to the
809+
* first argument of the `pageFunction`.
810+
* By default it is typed as `Element[]`, but you may need to provide a more
811+
* specific sub-type:
812+
*
802813
* @example
803-
* ```js
804-
* const options = await page.$$eval(
805-
* 'div > span.options', options => options.map(option => option.textContent));
814+
*
815+
* ```
816+
* // if you don't provide HTMLInputElement here, TS will error
817+
* // as `value` is not on `Element`
818+
* await page.$$eval('input', (elements: HTMLInputElement[]) => {
819+
* return elements.map(e => e.value);
820+
* });
806821
* ```
807822
*
808-
* @param selector - the
823+
* The compiler should be able to infer the return type
824+
* from the `pageFunction` you provide. If it is unable to, you can use the generic
825+
* type to tell the compiler what return type you expect from `$$eval`:
826+
*
827+
* @example
828+
*
829+
* ```
830+
* // The compiler can infer the return type in this case, but if it can't
831+
* // or if you want to be more explicit, provide it as the generic type.
832+
* const allInputValues = await page.$$eval<string[]>(
833+
* 'input', (elements: HTMLInputElement[]) => elements.map(e => e.textContent)
834+
* );
835+
* ```
836+
*
837+
* @param selector the
809838
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
810839
* to query for
811-
* @param pageFunction - the function to be evaluated in the page context.
812-
* Will be passed the result of
813-
* `Array.from(document.querySelectorAll(selector))` as its first argument.
814-
* @param args - any additional arguments to pass through to `pageFunction`.
840+
* @param pageFunction the function to be evaluated in the page context. Will
841+
* be passed the result of `Array.from(document.querySelectorAll(selector))`
842+
* as its first argument.
843+
* @param args any additional arguments to pass through to `pageFunction`.
815844
*
816-
* @returns The result of calling `pageFunction`.
845+
* @returns The result of calling `pageFunction`. If it returns an element it
846+
* is wrapped in an {@link ElementHandle}, else the raw value itself is
847+
* returned.
817848
*/
818-
async $$eval<ReturnType extends any>(
849+
async $$eval<ReturnType>(
819850
selector: string,
820-
pageFunction: EvaluateFn | string,
851+
pageFunction: (
852+
elements: Element[],
853+
/* These have to be typed as unknown[] for the same reason as the $eval
854+
* definition above, please see that comment for more details and the TODO
855+
* that will improve things.
856+
*/
857+
...args: unknown[]
858+
) => ReturnType | Promise<ReturnType>,
821859
...args: SerializableOrJSHandle[]
822-
): Promise<ReturnType> {
860+
): Promise<WrapElementHandle<ReturnType>> {
823861
return this.mainFrame().$$eval<ReturnType>(selector, pageFunction, ...args);
824862
}
825863

Diff for: ‎test/queryselector.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ describe('querySelector', function () {
218218
'<html><body><div class="tweet"><div class="like">100</div><div class="like">10</div></div></body></html>'
219219
);
220220
const tweet = await page.$('.tweet');
221-
const content = await tweet.$$eval('.like', (nodes) =>
221+
const content = await tweet.$$eval('.like', (nodes: HTMLElement[]) =>
222222
nodes.map((n) => n.innerText)
223223
);
224224
expect(content).toEqual(['100', '10']);
@@ -231,7 +231,7 @@ describe('querySelector', function () {
231231
'<div class="a">not-a-child-div</div><div id="myId"><div class="a">a1-child-div</div><div class="a">a2-child-div</div></div>';
232232
await page.setContent(htmlContent);
233233
const elementHandle = await page.$('#myId');
234-
const content = await elementHandle.$$eval('.a', (nodes) =>
234+
const content = await elementHandle.$$eval('.a', (nodes: HTMLElement[]) =>
235235
nodes.map((n) => n.innerText)
236236
);
237237
expect(content).toEqual(['a1-child-div', 'a2-child-div']);

0 commit comments

Comments
 (0)
Please sign in to comment.