Zurück
Tech 4 Min. Lesezeit - 7. Aug. 22 - Mayeul Le Monies de Sagazan

TypeScript-Verzögerungen bei i18n reduzieren

Dieser Artikel knüpft an einen anderen an, es wird empfohlen, diesen zuerst zu lesen: pourquoi-mon-projet-typescript-est-lent.
Im letzten Artikel haben wir gesehen, wie man die Code-Teile findet, die bei der TypeScript-Kompilierung am längsten dauern.
In dem gegebenen Beispiel, haben wir festgestellt, dass TypeScript über 3 Sekunden brauchte, um einen Teil des Codes zu typechecken, der die i18next-Internationalisierungsbibliothek verwendet, (was ziemlich ärgerlich ist, wenn man 3 Sekunden warten muss, bevor man die Autovervollständigung für Übersetzungsschlüssel erhält).
Wir werden dieses Problem in 2 Schritten lösen:
  • Einen Skript erstellen, um die Union aller Übersetzungsschlüssel zu generieren
  • Einen Wrapper um die i18next-API erstellen, um unsere eigene Union für den Typ der Übersetzungsschlüssel zu verwenden

Warum diese Union generieren, i18next kann das doch selbst, oder nicht?

Ja, aber es generiert diese Union, indem es die Unions aller i18n-Workspaces zusammenführt. Hier haben wir 8 mit je 150 Schlüsseln pro Workspace. Es ist jedoch die Zusammenführung der Unions der verschiedenen Workspaces, die am längsten zu dauern scheint:
"[...] For instance, to eliminate redundant members from a union, the elements have to be compared pairwise, which is quadratic. This sort of check might occur when intersecting large unions, where intersecting over each union member can result in enormous types that then need to be reduced." (TypeScript performance wiki)
Deshalb möchte ich diese Union mit einem Skript erstellen, anstatt mehrere Unions mit TypeScript zusammenzuführen.
Sie können auf den Code dieses Beispiels zugreifen hier.

Das Skript:

const { writeFileSync } = require("fs");

const anotherTest = require("./fr/anotherTest.json");
const hehe = require("./fr/hehe.json");
const login = require("./fr/login.json");
const menu = require("./fr/menu.json");
const mobile = require("./fr/mobile.json");
const sample = require("./fr/sample.json");
const userErrors = require("./fr/userErrors.json");
const common = require("./fr/common.json");

const keys = [
  ...Object.keys(anotherTest).map((key) => `anotherTest:${key}`),
  ...Object.keys(hehe).map((key) => `hehe:${key}`),
  ...Object.keys(login).map((key) => `login:${key}`),
  ...Object.keys(menu).map((key) => `menu:${key}`),
  ...Object.keys(mobile).map((key) => `mobile:${key}`),
  ...Object.keys(sample).map((key) => `sample:${key}`),
  ...Object.keys(userErrors).map((key) => `userErrors:${key}`),
  ...Object.keys(common).map((key) => `common:${key}`),
];

const keysText = keys.map((key) => `| '${key}'`).join("\n");
const fileText = `export type AllKeysUnion = \n${keysText};\n`;
const FILE_NAME = "AllKeysUnion.ts";

writeFileSync(FILE_NAME, fileText, "utf8");
Dieses Skript besteht aus 3 Schritten:
  • wir importieren die JSON-Dateien, die die uns interessierenden Schlüssel enthalten
  • wir formatieren die uns interessierenden Informationen in einem grossen String (die Variable fileText)
  • wir erstellen die Datei mit fs.writeFile
Dies ergibt eine Datei, die AllKeysUnion exportiert, welche eine Union ist, deren jeder String das Format "workspace:Schlüssel"
export type AllKeysUnion =
  | "anotherTest:anotherTest-0"
  | "anotherTest:anotherTest-1"
  | "anotherTest:anotherTest-2"
...

Der Wrapper:

import { i18n as I18nInterface, TOptions } from "i18next";
import { useTranslation as useTranslationOriginal } from "react-i18next";
import { AllKeysUnion } from "./AllKeysUnion";

export type TFunctionWrapper = (
  key: AllKeysUnion,
  options?: TOptions
) => string;

export type UseTranslationResponse = {
  t: TFunctionWrapper;
  i18n: I18nInterface;
  ready: boolean;
};

export function useTranslationWrapper(): UseTranslationResponse {
  const { t, i18n, ready } = useTranslationOriginal();

  const myT = (key: AllKeysUnion, options?: TOptions) => t(key, options);

  return { t: myT, i18n, ready };
}
In dieser Datei ist unser Wrapper das Wichtigste: useTranslationWrapper, diese Funktion werden wir anstelle des useTranslation von i18next in unserem React-Code.
Diese Funktion wird das useTranslation Original von i18next verwenden, ist aber mit der TypeScript-Union typisiert, die wir mit dem zuvor erstellten Skript generiert haben.

Verwenden des Wrappers:

Es bleibt uns nur noch, diesen Wrapper in unserem React-Code zu verwenden:
Dazu müssen Sie lediglich die Aufrufe an useTranslation durch Aufrufe an useTranslationWrapper.
In App.tsx :
const { t } = useTranslationWrapper();
Das ist alles, was wir ändern müssen.

Überprüfen Sie, ob die Verzögerungen verschwunden sind :

Um sicherzustellen, dass all diese Arbeit nicht umsonst war, werden wir die gleichen Tools wie in dem vorherigen Artikel verwenden, um die Kompilierungs-Berichtsdateien vor und nach den vorgenommenen Änderungen zu generieren.
Sobald die Kompilierungsberichte generiert sind, müssen wir nur noch das Tool @typescript/analyze-trace verwenden, um das Ergebnis zu betrachten.
Vorher :
Hot Spots
└─ Check file /home/mayeul/Projects/tests/test-tsc/src/App.tsx (3700ms)
   └─ Check deferred node from (line 15, char 9) to (line 16, char 33) (3186ms)
      └─ Check expression from (line 16, char 10) to (line 16, char 20) (3172ms)
         └─ Check expression from (line 16, char 21) to (line 16, char 29) (3170ms)
            └─ Check expression from (line 16, char 22) to (line 16, char 28) (3170ms)
Nachher :
Found nothing in 1 project(s)
Das Tool erkennt keinen besonders langsam zu kompilierenden Code mehr, und das Projekt kompiliert in weniger als 1 Sekunde, verglichen mit über 3 Sekunden zuvor !
Hinweis : Für diesen Artikel haben wir einen sehr minimalistischen Wrapper erstellt. Wir haben zum Beispiel keine Übersetzungsfunktion für einen einzelnen Workspace ermöglicht, was mit i18next möglich ist : useTranslation(workspace), dennoch hält uns nichts davon ab, den Wrapper zu verbessern.

Möchten Sie bei der Lancierung Ihres digitalen Projekts begleitet werden?

Reichen Sie Ihr Projekt jetzt ein