diff --git a/src/TransWithoutContext.js b/src/TransWithoutContext.js index 9260f315d..a2858aeba 100644 --- a/src/TransWithoutContext.js +++ b/src/TransWithoutContext.js @@ -1,6 +1,6 @@ import { Fragment, isValidElement, cloneElement, createElement, Children } from 'react'; import HTML from 'html-parse-stringify'; -import { isString, warn, warnOnce } from './utils.js'; +import { isObject, isString, warn, warnOnce } from './utils.js'; import { getDefaults } from './defaults.js'; import { getI18n } from './i18nInstance.js'; @@ -78,7 +78,7 @@ export const nodesToString = (children, i18nOptions) => { } } else if (child === null) { warn(`Trans: the passed in value is invalid - seems you passed in a null child.`); - } else if (typeof child === 'object') { + } else if (isObject(child)) { // e.g. lorem {{ value, format }} ipsum const { format, ...clone } = child; const keys = Object.keys(clone); @@ -124,7 +124,7 @@ const renderNodes = (children, targetString, i18n, i18nOptions, combinedTOpts, s childrenArray.forEach((child) => { if (isString(child)) return; if (hasChildren(child)) getData(getChildren(child)); - else if (typeof child === 'object' && !isValidElement(child)) Object.assign(data, child); + else if (isObject(child) && !isValidElement(child)) Object.assign(data, child); }); }; @@ -203,12 +203,10 @@ const renderNodes = (children, targetString, i18n, i18nOptions, combinedTOpts, s isElement && hasChildren(node, true) && !node.voidElement; const isEmptyTransWithHTML = - emptyChildrenButNeedsHandling && typeof child === 'object' && child.dummy && !isElement; + emptyChildrenButNeedsHandling && isObject(child) && child.dummy && !isElement; const isKnownComponent = - typeof children === 'object' && - children !== null && - Object.hasOwnProperty.call(children, node.name); + isObject(children) && Object.hasOwnProperty.call(children, node.name); if (isString(child)) { const value = i18n.services.interpolator.interpolate(child, opts, i18n.language); @@ -256,7 +254,7 @@ const renderNodes = (children, targetString, i18n, i18nOptions, combinedTOpts, s mem.push(`<${node.name}>${inner}`); } - } else if (typeof child === 'object' && !isElement) { + } else if (isObject(child) && !isElement) { const content = node.children[0] ? translationContent : null; // v1 diff --git a/src/useTranslation.js b/src/useTranslation.js index f180e881e..e28581dd3 100644 --- a/src/useTranslation.js +++ b/src/useTranslation.js @@ -1,6 +1,13 @@ import { useState, useEffect, useContext, useRef, useCallback } from 'react'; import { getI18n, getDefaults, ReportNamespaces, I18nContext } from './context.js'; -import { warnOnce, loadNamespaces, loadLanguages, hasLoadedNamespace, isString } from './utils.js'; +import { + warnOnce, + loadNamespaces, + loadLanguages, + hasLoadedNamespace, + isString, + isObject, +} from './utils.js'; const usePrevious = (value, ignore) => { const ref = useRef(); @@ -31,11 +38,7 @@ export const useTranslation = (ns, props = {}) => { warnOnce('You will need to pass in an i18next instance by using initReactI18next'); const notReadyT = (k, optsOrDefaultValue) => { if (isString(optsOrDefaultValue)) return optsOrDefaultValue; - if ( - optsOrDefaultValue && - typeof optsOrDefaultValue === 'object' && - isString(optsOrDefaultValue.defaultValue) - ) + if (isObject(optsOrDefaultValue) && isString(optsOrDefaultValue.defaultValue)) return optsOrDefaultValue.defaultValue; return Array.isArray(k) ? k[k.length - 1] : k; }; diff --git a/src/utils.js b/src/utils.js index 34552444b..1a19fd81d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -130,3 +130,5 @@ export const getDisplayName = (Component) => (isString(Component) && Component.length > 0 ? Component : 'Unknown'); export const isString = (obj) => typeof obj === 'string'; + +export const isObject = (obj) => typeof obj === 'object' && obj !== null; diff --git a/test/utils.spec.js b/test/utils.spec.js index ca63b2c8c..7753a4d7c 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { isString } from '../src/utils.js'; +import { isString, isObject } from '../src/utils.js'; describe('isString', () => { it('should return true for strings', () => { @@ -13,3 +13,19 @@ describe('isString', () => { }, ); }); + +describe('isObject', () => { + it.each([[{}], [{ key: 'value' }], [[]]])( + 'should return true for objects, testing %o', + (value) => { + expect(isObject(value)).toBe(true); + }, + ); + + it.each([[undefined], [null], [1], ['string'], [() => {}]])( + 'should return false for non-objects, testing %o', + (value) => { + expect(isObject(value)).toBe(false); + }, + ); +});