From 9109e04b5838f09c37eeef22c17c8ec27bf125e4 Mon Sep 17 00:00:00 2001 From: Marcos Gomez Castillo Date: Tue, 31 Aug 2021 14:50:06 +0200 Subject: [PATCH] feat(types): allow key separator augmentation --- test/typescript/returnTypes.test.ts | 31 ++++++++++++++++++++++++++++- ts4.1/index.d.ts | 19 +++++++++++++----- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/test/typescript/returnTypes.test.ts b/test/typescript/returnTypes.test.ts index 1fd7985e..6b0691aa 100644 --- a/test/typescript/returnTypes.test.ts +++ b/test/typescript/returnTypes.test.ts @@ -1,4 +1,4 @@ -import { NormalizeByTypeOptions } from 'react-i18next'; +import { AppendKeys, NormalizeByTypeOptions, NormalizeReturn, TFuncReturn } from 'react-i18next'; // Test cases for TypeOptions['returnNull']: true type ReturnNull = NormalizeByTypeOptions; // Returns null @@ -31,3 +31,32 @@ const emptyStringValue2: ReturnEmptyString = ''; // @ts-expect-error: '"non-empty-string"' is not assignable to type '""' const nonEmptyStringValue2: ReturnEmptyString = 'non-empty-string'; + +// Test cases for TypeOptions['keySeparator']: '.' (default) +type DefaultCase = AppendKeys<'namespace', 'key' | 'key2'>; +const defaultCaseExpectedResult = 'namespace.key'; +const defaultCaseExpectedResult2 = 'namespace.key2'; +const defaultCase: DefaultCase = defaultCaseExpectedResult; +const defaultCase2: DefaultCase = defaultCaseExpectedResult2; + +// Test cases for TypeOptions['keySeparator']: '>>>' (arbitrary separator) +type ArbitrarySeparatorCase = AppendKeys<'namespace', 'key' | 'key2', '>>>'>; +const arbitrarySeparatorExpectedResult = 'namespace>>>key'; +const arbitrarySeparatorExpectedResult2 = 'namespace>>>key2'; +const arbitrarySeparatorCase: ArbitrarySeparatorCase = arbitrarySeparatorExpectedResult; +const arbitrarySeparatorCase2: ArbitrarySeparatorCase = arbitrarySeparatorExpectedResult2; + +// Test cases for TypeOptions['keySeparator']: false (nesting not supported) +interface MockDictionary { key: { nested: 'value' }; notNested: 'value' }; + +type ReturnGivenKey = NormalizeReturn; +const shouldBeGivenKey: ReturnGivenKey = 'key.nested'; + +type ReturnGivenKey2 = NormalizeReturn; +const shouldBeGivenKey2: ReturnGivenKey2 = 'keyfalsenested'; + +type ReturnValue = NormalizeReturn; +const shouldBeTranslationValue: ReturnValue = 'value'; + +type ReturnValue2 = NormalizeReturn; +const shouldBeTranslationValue2: ReturnValue2 = 'value'; diff --git a/ts4.1/index.d.ts b/ts4.1/index.d.ts index fd2051c6..8ace2771 100644 --- a/ts4.1/index.d.ts +++ b/ts4.1/index.d.ts @@ -54,6 +54,7 @@ type TypeOptions = MergeBy< { returnNull: true; returnEmptyString: true; + keySeparator: '.'; defaultNS: 'translation'; resources: Resources; }, @@ -92,8 +93,10 @@ declare module 'i18next' { } // Normalize single namespace -type AppendKeys = `${K1 & string}.${K2 & string}`; -type AppendKeys2 = `${K1 & string}.${Exclude & string}`; +type AppendKeys = `${K1 & string}${S}${K2 & + string}`; +type AppendKeys2 = `${K1 & + string}${S}${Exclude & string}`; type Normalize2 = K extends keyof T ? T[K] extends Record ? T[K] extends readonly any[] @@ -135,12 +138,18 @@ export type NormalizeByTypeOptions< R = TypeOptionsFallback > = TypeOptionsFallback; -type NormalizeReturn = V extends `${infer K}.${infer R}` +type NormalizeReturn< + T, + V, + S extends string | false = TypeOptions['keySeparator'] +> = V extends keyof T + ? NormalizeByTypeOptions + : S extends false + ? V + : V extends `${infer K}${S}${infer R}` ? K extends keyof T ? NormalizeReturn : never - : V extends keyof T - ? NormalizeByTypeOptions : never; type NormalizeMultiReturn = V extends `${infer N}:${infer R}`