2

This code runs fine, i.e. executes the method called import_codeData on the class DataIimporter by dynamically calling this.import_codeData() based on the content of the string variable task:

enter image description here

However, in Visual Studio Code, TypeScript shows this error:

enter image description here

It gets the same error if a simple string is sent:

enter image description here

While this code works fine, how can I get TypeScript not to display this error in the editor?


ADDENDUM:

I reproduced this error online here at TypeScript Playground:

class DataImporter {

    task: string;

    constructor() {
        this.task = "import_data001";
    }

    test() {
        this[this.task]();
    }

    import_data001() {
        console.log('importing data001');
    }
}

const dataImporter = new DataImporter();
dataImporter.import_data001();
dataImporter.test();
Edward Tanguay
  • 189,012
  • 314
  • 712
  • 1,047
  • 1
    Minimal reproducible example on https://www.typescriptlang.org/play would be helpful – Aleksey L. Oct 29 '20 at 12:23
  • here you go: https://www.typescriptlang.org/play?#code/FAYwNghgzlAEAiEAuECSBbADgewE5IFNdYBvYYWS2FKAawC5YolcBLAOwHMBucq2ENnbNcAVxBI8ACgCUpCvypIAFqygA6GrVgBeWACJWWPEgD6AE2QQADNYCM+3vwC+fJQWaz5ipaqgBtFTVNaFoAXVknKlcFSiMcfAsrWzsvMh9KQWFsMAJ1MGxOKQByeJMOTlhLFBTimSjKVxis5iqrDATCYj12AgB3BHbjfCJI4Gq0Ya71MsSJlLGJjpMiTQ8kSKA – Edward Tanguay Oct 29 '20 at 12:29

3 Answers3

1

Solution on the playground

The type of task needs to be more specific than just "string", specifically keyof DataImporter. But when you do that, it can also refer to keys that name non-methods, so you'd need to exclude those when trying to invoke them, or as I did, check directly that they are functions.

Type of fn:

const fn: "task" | "test" | "import_data001" | (() => void) | (() => void)

Type of fn after only considering functions:

const fn: () => void


class DataImporter {

    task: keyof DataImporter;

    constructor() {
        this.task = "import_data001";
    }

    test() {
        const fn = this[this.task];
        if (typeof fn === 'function') { // or !== 'string'
            fn();
        }
    }

    import_data001() {
        console.log('importing data001');
    }
}

const dataImporter = new DataImporter();
dataImporter.import_data001();
dataImporter.test();

Partial credit to https://stackoverflow.com/a/56894533/823470 for helping with the first issue. I solved the second just by looking at the inferred type of fn and seeing that I wanted to only consider functions.

tar
  • 1,538
  • 1
  • 20
  • 33
0
test() {
        (this[this.task as keyof DataImporter] as Function)();
}
  • We apply the keyof operator to this.task and thus telling the compiler that it this.task represents a property of the DataImporter class
  • We're applying Function to this[this.task as keyof DataImporter] thus asserting that it's a Function.
Malbolged
  • 109
  • 1
  • 5
0

You could do something like this (playground):

type GetLength<T extends any[]> = T extends { length: infer L } ? L : never

type ZeroLength<T> = {
  [K in keyof T]: T[K] extends () => void  ? (GetLength<Parameters<T[K]>> extends 0 ? K : never): never;
}[keyof T]

type Callable = ZeroLength<Foo>

class Foo {
  doA() {

  }

  doB() {

  }

  doC() {

  }

  multipleArgs(a: number, b: number) {
    return a + b;
  }

  runSomething(fnName: Callable) {
    this[fnName]();
  }
}
Evan Trimboli
  • 29,900
  • 6
  • 45
  • 66
  • `T[K] extends () => void ? K : never;` is enough, it already covers params length check https://www.typescriptlang.org/play?#code/C4TwDgpgBAWhBOB7AMhAdgc2ACwDwBUA+KAXigG8AoKKAbQGkoBLNKAawhEQDMp8BdAFx8G-KBAAewdABMAzlAAUASlLEAboiYyaAfiiNhaCOoQBuSgF9aHLrwGVKoSFADCAQwA2n9wCNP0GRwSKiYOLgAYoiIhI4Axj5yClGIFNRQMogAgipp6ZaONJkAQrlU+YUZiK5llQXpALYArp7ATGABWfAYcoruRk0NvggANFC+A0MIqlQ0NPAQwE3wrO5QANTjFjT1801oAMqIDYvYLBiK3GgAcu4nwh7efgEz6TQ4THK0V7cn-CrbKAFSxAA – Aleksey L. Oct 30 '20 at 05:04