reactjs - Hur man anropar en klassmetod dynamiskt med en variabel samtidigt som man undviker TypeScript-fel 7053?


Denna kod körs bra, det vill säga utför metoden som heter import_codeData på klassen DataIimporter genom att dynamiskt anropa this.import_codeData() baserat på innehållet i strängvariabeln task:

Men i Visual Studio Code visar TypeScript detta fel:

Det får samma fel om en enkel sträng skickas:

Även om den här koden fungerar bra, hur kan jag få TypeScript att inte visa det här felet i editorn?


TILLÄGG:

Jag reproducerade detta fel online här på 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();

Svar #1

Lösning på lekplatsen

Typen av task behöver vara mer specifik än bara "string", specifikt keyof DataImporter. Men när du gör det kan det också referera till nycklar som namnger icke-metoder, så du skulle behöva utesluta dem när du försöker anropa dem, eller som jag gjorde, kontrollera direkt att de är funktioner.

Typ av fn:

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

Typ av fn efter att endast ha tagit hänsyn till funktioner:

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();

Delvis kredit till https://stackoverflow.com/a/56894533/823470 för hjälp med det första problemet. Jag löste det andra bara genom att titta på den härledda typen av fn och se att jag bara ville ta hänsyn till funktioner.

Svar #2

Du skulle kunna göra något liknande detta (lekplats):

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]();
  }
}