typesafe-i18n
typesafe-i18n copied to clipboard
Using `extendDictionary` does not work well with nested namespaces.
Version
5.26.2
Describe the bug
If you extend a dictionary (extendDictionary
) and set a value in a nested namespace, it erases that whole namespace instead of just extending the one value. Details represented by the tests below.
It is unlikely you ever intend to erase values when extending a dictionary, you typically only override. So the default behavior is counter to that.
You can explicitly extend each nested namespace by hand extending, but only if you ignore the generated extendDictionary
function that "hides" the original, and then use the original.
Reproduction
Taking the test cases for this utility method and adding this test case, it fails:
simple: 'Hello',
nested: { value: 'Hello nested' },
nested2: { value1: "One value", value2: "Another value", value3: "And yet another value"},
deeper: { nesting: { value1: "v1", value2: "v2" }}
}
test('nested extend with more complex', () => {
const extended = extendDictionary(translationComplex, {
simple: 'Hello extended',
nested2: { value2: 'Another value extended' },
deeper: { nesting: { value2: 'v2 extended'}}
})
assert.equal(extended, {
simple: 'Hello extended',
nested: { value: 'Hello nested' },
nested2: { value1: "One value", value2: "Another value extended", value3: "And yet another value"},
deeper: { nesting: { value1: "v1", value2: "v2 extended" } }
});
})
You can actually do this to pass:
test('nested extend explicit at each level with more complex', () => {
const extended = extendDictionary(translationComplex, {
simple: 'Hello extended',
nested2: extendDictionary(translationComplex.nested2, { value2: 'Another value extended' }),
deeper: extendDictionary(translationComplex.deeper, { nesting:
extendDictionary(translationComplex.deeper.nesting, { value2: 'v2 extended'})
})
})
assert.equal(extended, {
simple: 'Hello extended',
nested: { value: 'Hello nested' },
nested2: { value1: "One value", value2: "Another value extended", value3: "And yet another value"},
deeper: { nesting: { value1: "v1", value2: "v2 extended" } }
});
})
but this is really awkward. And is more so because the symbol extendDictionary
is replaced in local generated code meaning you have to ignore that, and import the original version to make it work.
The original tests by @osdiab were not sufficient to be clear what was intended here.
Logs
No response
Config
No response
Additional information
No response
The fix is to change:
extend({}, base, part) as Translation
to:
extend(true, {}, base, part) as Translation
workaround is to add your own utility function:
export const initExtendLanguage =
<TranslationType extends BaseTranslation>() =>
<Base extends BaseTranslation | TranslationType, Translation extends Base>(
base: Base,
part: DeepPartial<ToGenericString<Translation>>,
): Translation =>
extend(true, {}, base, part) as Translation
export const extendLanguage = initExtendLanguage()
and pnpm install [email protected]
to use the same utility class already in the base library.
I fix it by use deep-extend library