From fdaf005846f7d4b6b40ccf82fd31e1c039623430 Mon Sep 17 00:00:00 2001 From: "Grigorii K. Shartsev" Date: Sun, 6 Oct 2024 17:11:40 +0200 Subject: [PATCH] fix(types): type translation variable keys Signed-off-by: Grigorii K. Shartsev --- lib/translation.ts | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/translation.ts b/lib/translation.ts index dc3150e..378cd71 100644 --- a/lib/translation.ts +++ b/lib/translation.ts @@ -31,8 +31,17 @@ interface TranslationVariableReplacementObject { escape: boolean } +/** + * Extracts variables from a translation key + * @notExported + */ +type ExtractedVariables = + T extends `${string}{${infer Variable}}${infer Rest}` + ? Variable | ExtractedVariables + : never + /** @notExported */ -type TranslationVariables = Record> +type TranslationVariables = Record, string | number | TranslationVariableReplacementObject> /** * Translate a string @@ -47,10 +56,10 @@ type TranslationVariables = Record( app: string, - text: string, - vars?: TranslationVariables, + text: T, + vars?: TranslationVariables, number?: number, options?: TranslationOptions, ): string { @@ -71,7 +80,7 @@ export function translate( // TODO: cache this function to avoid inline recreation // of the same function over and over again in case // translate() is used in a loop - const _build = (text: string, vars?: TranslationVariables, number?: number) => { + const _build = (text: string, vars?: TranslationVariables, number?: number) => { return text.replace(/%n/g, '' + number).replace(/{([^{}]*)}/g, (match, key) => { if (vars === undefined || !(key in vars)) { return optEscape(match) @@ -119,12 +128,12 @@ export function translate( * @param {object} vars of placeholder key to value * @param {object} options options object */ -export function translatePlural( +export function translatePlural( app: string, - textSingular: string, - textPlural: string, + textSingular: T, + textPlural: K, number: number, - vars?: Record, + vars?: TranslationVariables & TranslationVariables, options?: TranslationOptions, ): string { const identifier = '_' + textSingular + '_::_' + textPlural + '_' @@ -139,10 +148,11 @@ export function translatePlural( } } + // vars type is casted to allow extra keys without runtime filtering (they are harmless), and without allowing wrong keys in translate if (number === 1) { - return translate(app, textSingular, vars, number, options) + return translate(app, textSingular, vars as TranslationVariables, number, options) } else { - return translate(app, textPlural, vars, number, options) + return translate(app, textPlural, vars as TranslationVariables, number, options) } }