Search code examples
javascripttypescriptimportlocaledate-fns

Failing to "dynamically" import date-fns/locale libs - TypeScript giving an attempted import error


I have an app that receives a list of supported locales from the backend as a following response:

{locales: [{code: 'enUS'}, {code: 'deDE'}, {code: 'arAR'}]}

I want to use date-fns library for handling date formatting but I have to import the whole date-fns/locale as I can't know beforehand which locale will be needed:

import * as dateFnsLocales from 'date-fns/locale';

The problem is, some of the locales are in different code format (for example, support for deutsch language is enabled when the backend response includes code: 'deDE', but the corresponding date-fns package is just 'de'. On the other hand date-fns package for english is 'enUS', not just 'en'.

Easy solution imho would be to handle it with some coalescing operator. The example is following:

import * as dateFnsLocales from 'date-fns/locale';

const supportedLocales = {locales: [{code: 'enUS'}, {code: 'deDE'}, {code: 'plPL'}]}
const newArrayWithSupportedLocales = supportedLocales.locales.map((locale) => ({
        ...locale,
        dateFnsLocale: (dateFnsLocales[locale.code] || dateFnsLocales[locale.code.substring(0,2)]),
      }));

Unfortunately I get the typescript error: No index signature with a parameter of type 'string' was found on type 'typeof import("date-fns/locale")'. TS7053

Even if I hardcode the attempt like so:

dateFnsLocale: dateFnsLocales['plPL'.substring(0,2)]

it fails with the same error, even though this:

dateFnsLocale: dateFnsLocales['pl']

works just fine.


Solution

  • Here's the code I am using for doing dynamic lookups using Expo's Localization object.

    import * as Localization from 'expo-localization';
    import * as Locales from 'date-fns/locale';
    import { Locale } from 'date-fns';
    
    /**
     * Looks up a date-fns locale from the Expo localization object.  This falls back to `en-US`
     * @param localization Expo Localization object containing the locale and region.
     * @returns date-fns locale.
     */
    export function getDateFnsLocale({ locale, region }: Pick<typeof Localization, 'locale'|'region'>) : Locale {
      return (
        Locales[locale.substring(0, 2) + region] ?? Locales[locale.substring(0, 2)] ?? Locales.enUS
      );
    }
    

    Here's the test

    import { enUS, fr, frCA } from 'date-fns/locale';
    
    describe('date-fns locale lookup', () => {
      it('shound find fr', () => {
        expect(getDateFnsLocale({ locale: 'fr', region: null })).toBe(fr);
      });
      it('shound find fr-CA', () => {
        expect(getDateFnsLocale({ locale: 'fr-CA', region: 'CA' })).toBe(frCA);
      });
      it('shound not find zz-ZZ', () => {
        expect(getDateFnsLocale({ locale: 'zz-ZZ', region: 'ZZ' })).toBe(enUS);
      });
    });