You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复Puppeteer+TypeScript自定义innerText获取方法的编译错误

修复Puppeteer自定义getInnerText函数的TypeScript编译错误

问题背景

我原本用这段代码获取HTML元素的innerText:

import puppeteer, { Page } from 'puppeteer';

const main = async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(url);

  const book_title = await page.$eval(
      'h1.Text__title1',
      (el: HTMLHeadingElement) => el.innerText
  );
}

为了复用逻辑,我写了getInnerText方法:

async function getInnerText<T extends HTMLElement>(
    page: Page,
    selector: string
): Promise<string | null> {
    const innerText = await page.$eval(
        selector,
        (el: T) => el.innerText
    ) as string | null;
    return innerText;
}

调用方式:

const book_title = await getInnerText<HTMLHeadingElement>(page, 'h1.Text__title1')

但VS Code里(el: T) => el.innerText处出现编译错误:

Argument of type '(el: T) => string' is not assignable to parameter of type 'string | EvaluateFuncWith<Element, []>'.
Type '(el: T) => string' is not assignable to type 'EvaluateFuncWith<Element, []>'.
Types of parameters 'el' and 'params_0' are incompatible.
Type 'Element' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Element'.

错误原因

TypeScript类型检查器认为:虽然你声明了T extends HTMLElement,但page.$eval的回调默认接收的参数类型是Element,而泛型T作为HTMLElement的子类型,约束比Element更严格,Element无法直接赋值给T,因此出现类型不兼容的报错。

修复方案

方案1:回调内对元素做类型断言

直接在回调中把Element断言为T,明确告知TypeScript该元素符合泛型T的类型:

async function getInnerText<T extends HTMLElement>(
    page: Page,
    selector: string
): Promise<string | null> {
    const innerText = await page.$eval(
        selector,
        (el: Element) => (el as T).innerText
    );
    return innerText ?? null;
}

方案2:用page.$ + evaluate替代page.$eval

先通过page.$获取元素句柄,再调用evaluate处理,这种方式更直观处理元素不存在的场景:

async function getInnerText<T extends HTMLElement>(
    page: Page,
    selector: string
): Promise<string | null> {
    const elementHandle = await page.$(selector);
    if (!elementHandle) return null;
    
    const innerText = await elementHandle.evaluate((el: T) => el.innerText);
    await elementHandle.dispose(); // 释放资源
    return innerText;
}

方案3:简化泛型约束

让泛型T直接继承Element,利用TypeScript的类型兼容性简化约束:

async function getInnerText<T extends Element>(
    page: Page,
    selector: string
): Promise<string | null> {
    const innerText = await page.$eval(
        selector,
        (el: T) => el.innerText
    );
    return innerText ?? null;
}

调用时依然可以传入HTMLHeadingElement作为泛型参数,因为它是Element的子类型,完全符合约束要求。

说明

三种方案均可解决类型不兼容问题:方案1最简洁,适合快速修复;方案2在需要处理元素不存在的场景时逻辑更清晰;方案3简化了泛型约束的写法,降低类型复杂度,可根据实际需求选择。

内容的提问来源于stack exchange,提问作者Shashank

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:13:19