无法在Typescript中使用布尔值的promise作为可重构的构造?

时间:2017-06-20 14:16:03

标签: javascript typescript promise protractor

我正在将一个Protractor页面对象库从JavaScript转换为Typescript。我有一个静态实用程序方法刷新,直到元素存在(或达到超时限制)。

出于某种原因,我无法使用boolean有价值的承诺作为thenable构造,我无法弄清楚原因。错误消息为error TS2345: Argument of type '(value: boolean) => Promise<{}> | undefined' is not assignable to parameter of type '((value: boolean) => {} | IThenable<{}>) | undefined'. Type '(value: boolean) => Promise<{}> | undefined' is not assignable to type '(value: boolean) => {} | IThenable<{}>'. Type 'Promise<{}> | undefined' is not assignable to type '{} | IThenable<{}>'. Type 'undefined' is not assignable to type '{} | IThenable<{}>'.

源代码如下,注释显示编译错误发生的位置。

我在我转换为Typescript的框架的纯JavaScript版本中使用了这种机制。我需要做什么才能将此方法转换为将要编译的Typescript?

import {browser, element, ElementFinder, ExpectedConditions} from 'protractor';
import * as wd from 'selenium-webdriver';
import {By} from "selenium-webdriver";

export class ExtendedExpectedConditions {

    public static refreshUntilElementIsPresent(element: ElementFinder,  numberOfSeconds: number = 30000,
        refreshInterval: number = 5000): void {
        this.refreshElement(element, numberOfSeconds, refreshInterval);
    }

    protected static refreshElement(element: ElementFinder, numberOfSeconds: number = 30000, refreshInterval:
        number = 5000) {


        //This line throws the compile error
        element.isPresent().then(value => {
            if (!value) {
                if (numberOfSeconds <= 0) {
                    return new wd.promise.Promise(function (resolve, reject) {
                        reject('Element cannot be found after expected retry numbers');
                    })
                }
                browser.sleep(refreshInterval).then(() => {
                    browser.refresh().then(() => this.refreshUntilElementIsPresent(element, numberOfSeconds -
                    (refreshInterval / 1000)));
                });
            }
       });
    }
}

我的'package.json'如下:

{
  "name": "my-project",
  "version": "1.0.0",
  "description": "A test suite",
  "scripts": {
    "tsc": "tsc",
    "pretest": "npm run tsc",
    "test": "node_modules/protractor/bin/protractor tmp/conf.js",
    "start_selenium": "node_modules/protractor/node_modules/webdriver-manager/bin/webdriver-manager start",
    "update_selenium": "node_modules/protractor/node_modules/webdriver-manager/bin/webdriver-manager update"
  },
  "author": aperson@someplace.com,
  "dependencies": {
    "@types/jasmine": "^2.5.52",
    "@types/jasminewd2": "^2.0.2",
    "@types/node": "^7.0.31",
    "any-promise": "^1.3.0",
    "jasmine-reporters": "^2.2.1",
    "protractor": "^5.1.2",
    "typescript": "^2.3.4"
  }
}

我的tsconfig.json如下:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "declaration": false,
    "noImplicitAny": false,
    "outDir": "tmp",
    "types": ["jasmine", "node"],
    "strict": true
  },
  "exclude": [
    "node_modules"
  ]
}

我的conf.ts文件如下:

import {Config, browser, protractor} from "protractor";

let testsTimeout = 3600000;
let delayBrowserTimeInSeconds = 0;

export let config: Config = {
    framework: 'jasmine2',
    rootElement: 'body',
    seleniumServerJar:'../node_modules/protractor/node_modules/webdriver-manager/selenium/selenium-server-standalone-3.4.0.jar',
    chromeDriver: '../node_modules/protractor/node_modules/webdriver-manager/selenium/chromedriver_2.30',
    specs: ['tests/*Spec.js'],
    capabilities: {
        browserName: 'chrome',
        acceptSslCerts: true,
        trustAllSSLCertificates: true,
        chromeOptions: {
            args: ['--no-sandbox']
        },
    },
    baseUrl: 'https://www.someurl.com',

    jasmineNodeOpts: {
        defaultTimeoutInterval: testsTimeout,
        showColors: true,
        isVerbose: true
    },

    onPrepare: () => {

        let origFn = browser.driver.controlFlow().execute;

        browser.driver.controlFlow().execute = function () {
            let args = arguments;

            origFn.call(browser.driver.controlFlow(), function () {
                return protractor.promise.delayed(delayBrowserTimeInSeconds * 100);
            });

            return origFn.apply(browser.driver.controlFlow(), args);
        };

        setTimeout(() => {
            browser.driver.executeScript<[number, number]>(() => {
                return [
                    window.screen.availWidth,
                    window.screen.availHeight
            ];
            }).then((result: [number, number]) => {
                browser.driver.manage().window().setSize(result[0], result[1]);
                browser.driver.manage().window().maximize();
            });
        });

    },

    getPageTimeout: 120000,
    allScriptsTimeout: testsTimeout
};

2 个答案:

答案 0 :(得分:4)

你真正的问题似乎是类型库的作者对then方法的回调参数有一个破碎的定义。您在问题中稍微错误地输入了错误消息,这使得查看正在发生的事情变得更加困难。

src/test.ts(17,34): error TS2345: Argument of type '(value: boolean) => Promise<{}> | undefined' is not assignable to parameter of type '((value: boolean) => {} | IThenable<{}>) | undefined'.
  Type '(value: boolean) => Promise<{}> | undefined' is not assignable to type '(value: boolean) => {} | IThenable<{}>'.
    Type 'Promise<{}> | undefined' is not assignable to type '{} | IThenable<{}>'.
      Type 'undefined' is not assignable to type '{} | IThenable<{}>'.

因此,您向.then提供的回调类型为(value: boolean) => Promise<{}> | undefined,也就是说它返回Promise或返回undefined

.then()期望获得((value: boolean) => {} | IThenable<{}>) | undefined (这是你失去亲密关系的地方)&#39;在问题中)也就是说功能或没有回调。当你给它一个函数时我们可以忽略undefined,所以我们必须给它一个函数来返回一些东西或IThenable<something>。此代码的作者已决定then中的回调不允许返回undefined

因此,短期修复很简单:更改.then回调,以便始终返回结果。

长期解决方案是向Selenium Webdriver的typescript绑定的作者报告问题,并要求他们允许回调返回{} | IThenable<{}> | undefined

你应该找到这个代码为你编译:

export class ExtendedExpectedConditions {

    public static refreshUntilElementIsPresent(element: ElementFinder,  numberOfSeconds: number = 30000,
        refreshInterval: number = 5000): void {
        this.refreshElement(element, numberOfSeconds, refreshInterval);
    }

    protected static refreshElement(element: ElementFinder, numberOfSeconds: number = 30000, refreshInterval:
        number = 5000) {


        //This line throws the compile error
        element.isPresent().then(value => {
            if (!value) {
                if (numberOfSeconds <= 0) {
                    return wd.promise.rejected('Element cannot be found after expected retry numbers');
                }
                browser.sleep(refreshInterval).then(() => {
                    browser.refresh().then(() => this.refreshUntilElementIsPresent(element, numberOfSeconds -
                    (refreshInterval / 1000)));
                });
            }
            return 42; // webdriver `.then` callback must return a result.
       });
    }
}

此外,由于某些原因,webdriver Promise的类型定义不会声明对象本身包含的.resolve().reject()方法,但您可以使用{{1}函数是一种更简单的方法来创建一个立即被拒绝的承诺。

答案 1 :(得分:0)

您得到的错误是正确的,如果您查看规格,您会看到量角器@returns {Promise<boolean>},请参阅规格here

您是否听说过TypeScript中的async / await方法。它将使您的生活更轻松,您的代码更清洁。您的代码如下所示。这也应该消除你的打字错误。

public static refreshUntilElementIsPresent(element: ElementFinder, numberOfSeconds: number = 30000,
  refreshInterval: number = 5000): void {
  return this.refreshElement(element, numberOfSeconds, refreshInterval);
}

// Add async here
protected static async refreshElement(element: ElementFinder, numberOfSeconds: number = 30000, refreshInterval: number = 5000) {

  // Wait untill the promise is resolved
  const isPresent = await element.isPresent();
  // Use the resolved promise
  if (!isPresent) {
    if (numberOfSeconds <= 0) {
      // You can clean the Promise rejection and create 1 line of code
      return Promise.reject('Element cannot be found after expected retry numbers');
    }
    // Wait for the rest
    await browser.sleep(refreshInterval);
    await browser.refresh();
    return this.refreshUntilElementIsPresent(element, numberOfSeconds - (refreshInterval / 1000));
  }
};