error TS7053: Element implicitly has an 'any' type because expression + other TS errors

Viewed 656

We have automated checking for prettier/lint/typescript errors in our CI tooling (VSC/Github).

Recently one or two of the dependabot updates has started failing ts scripts in the process in several different places and different ts files. As I'm not a ts person and have taken this over from someone else, I'd like some help in understanding. I've looked at various other posts for the same error, but still don't know what to change in my instance, and I don't want to change something incorrectly.

  1. Error:
Error: salesforce-utils/src/bin/cancel-deploy.ts(19,29): error TS7053: Element implicitly has an 'any' type because expression of type '"save-progress-file"' can't be used to index type '{ [x: string]: unknown; "save-progress-file": string; _: (string | number)[]; $0: string; } | Promise<{ [x: string]: unknown; "save-progress-file": string; _: (string | number)[]; $0: string; }>'.
  Property 'save-progress-file' does not exist on type '{ [x: string]: unknown; "save-progress-file": string; _: (string | number)[]; $0: string; } | Promise<{ [x: string]: unknown; "save-progress-file": string; _: (string | number)[]; $0: string; }>'.

File cancel-deploy.ts, and it's line 19 it's moaning about this.saveProgressFile = argv["save-progress-file"];:

import yargs = require("yargs");

import { State } from "../deploy";

class Config {
  saveProgressFile: string;

  constructor() {
    const argv = yargs
      .scriptName("cancel-deploy")
      .describe(
        "save-progress-file",
        "Save a file with the progress that can be used to resume. Default: [cluster-developer-name].deploy-log.json."
      )
      .alias("save-progress-file", "s")
      .string("save-progress-file")
      .demandOption("save-progress-file").argv;

    this.saveProgressFile = argv["save-progress-file"];
  }
...etc
  1. Same error, different file, where it has an error for each of the last lines in this snippet:
  constructor() {
    const argv = yargs
      .scriptName("deploy")
      .describe("cluster-config-file", "The file configuring the SF2SF sync.")
      .alias("cluster-config-file", "f")
      .string("cluster-config-file")
      .required("cluster-config-file")
      .array("cluster-config-file")
      .describe("validate", "Only do the validate part of the deploy")
      .alias("validate", "v")
      .boolean("validate")
      .default("validate", false)
      .describe("quick", "Don't wait for job to complete")
      .alias("quick", "q")
      .boolean("quick")
      .default("quick", false)
      .describe("resume", "Resume the deploy described in the file")
      .alias("resume", "r")
      .string("resume")
      .string("test-level")
      .alias("test-level", "l")
      .describe("test-level", "Passed through to the sfdx validate command")
      .default("test-level", "RunLocalTests")
      .describe(
        "save-progress-file",
        "Save a file with the progress that can be used to resume. Default: [cluster-developer-name].deploy-log.json."
      )
      .alias("save-progress-file", "s")
      .string("save-progress-file")
      .describe("skip-validation", "Skip the validation process, just deploy")
      .boolean("skip-validation")
      .default("skip-validation", false).argv;

    this.clusterConfigFiles = argv["cluster-config-file"];
    this.validateOnly = argv["validate"];
    this.quick = argv["quick"];
    this.resume = argv["resume"];
    this.testLevel = argv["test-level"];
    this.saveProgressFile = argv["save-progress-file"];
    this.skipValidation = argv["skip-validation"];
  }

And several more ts files with the same setup of a constructor with args, then assigning them.

How do I fix these? Any ts updates are now failing our CI process because the scripts are no longer passing the check. I can turn off the typecheck, but would rather fix the files (if that's the better solution).

  1. I have another Dependabot update which is also throwing other errors:

Error: salesforce-utils/src/bin/subscribed-fields.ts(190,11): error TS2349: This expression is not callable

Error: salesforce-utils/src/bin/subscribed-fields.ts(197,22): error TS2571: Object is of type 'unknown'.

Error: salesforce-utils/src/bin/subscribed-fields.ts(207,11): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'Connection'.

Below is the part of the ts file where the errors occur:

(async (): Promise<void> => {
  const argv = new Config();

  const { clusterConfigFile, concurrency } = argv;

  const clusterConfig = await SyndexClusterConfig.fromPath(clusterConfigFile);

  const browser = await launch();

  const connections = flatten(
    await pAll(
      clusterConfig.usernames.map(
        (username) => (): Promise<Connection[]> =>
          loginAndGetConnections(browser, username)
      ),
      { concurrency }
    )
  ).filter((conn) => conn.isActive);

  const differences: SubscribedFieldUpdate[] = [];

  await pAll(
    connections.map(
      (connection) => (): Promise<void> =>
        checkDifferencesForConnectionSafely(
          argv,
          browser,
          connection,
          differences
        )
    ),
    { concurrency }
  );

  const result = differences.map(
    ({ connection, connectionObject, connectionField, newValue }) => ({
      username: connection.username,
      connection: connection.name,
      object: connectionObject.name,
      field: connectionField.name,
      oldValue: (connectionField.value && connectionField.value.name) || "",
      newValue: (newValue && newValue.name) || ""
    })
  );

  console.log(JSON.stringify(result, null, "  "));

  await browser.close();
})();
1 Answers

1 & 2:

If you take a closer look at that error message (and format it a bit to make it legible):

'"save-progress-file"' can't be used to index type
  '{
    [x: string]: unknown;
    "save-progress-file": string;
    _: (string | number)[];
    $0: string;
  }
  | Promise<{
    [x: string]: unknown;
    "save-progress-file": string;
    _: (string | number)[];
    $0: string;
  }>'.

So typescript believes that argv could be an object or a promise. And if it's a promise, then thePromise["save-progress-file"] would fail.

In fact, if you hover over just yargs.argv you'll see this type:

const argv = yargs.argv // hover this to get the below type

(property) yargs.Argv<{}>.argv: {
    [x: string]: unknown;
    _: (string | number)[];
    $0: string;
} | Promise<{
    [x: string]: unknown;
    _: (string | number)[];
    $0: string;
}>

So to fix this you'll need to handle the case where argv is a promise. You can do that by awaiting it. If the value is a promise, it'll await, and if not it will proceed immediately.

const argv = await yargs.argv // hover this const to get the below type

{
    [x: string]: unknown;
    _: (string | number)[];
    $0: string;
} | {
    [x: string]: unknown;
    _: (string | number)[];
    $0: string;
}

Slightly odd reporting of the type here, but as you can see, no more promise!

However, these are in constructors, and class constructors cannot be async. This means you'll have to refactor your code significantly to allow this to be async.

Or if you know this will never be a promise because of how you are using the API, then you could try to detect if it's a promise and then throw an error. Then Typescript should be able to proceed assuming it's not a promise.

Why yargs.argv can be a promise and how it expects you to handle that, I know not. You should consult the documentation of that library.


I'm not going to dive into #3 since this is a big post already. But I advise you to go through the same process. Examine the error carefully, copy/paste it, format it until it can be easily reasoned about. Hover over the variables in your editor and note the types. Then finally fix your usage of the APIs in question to be correct.

Related