Skip to content

Is it possible to keep a strict type argument on Promise when using an import() with a computed string? #56242

@gustaveWPM

Description

@gustaveWPM

Acknowledgement

  • I acknowledge that issues using this template may be closed without further explanation at the maintainer's discretion.

Comment

Hello!
I asked this question about ten days ago on Stack Overflow, but I didn't get an answer, nor did I get an upvote or a downvote on my post...

Maybe I didn't ask my question properly and wasn't clear enough?
Please, don't hesitate to tell me if it is the case.

I'm going to ask it again here in the hope that I can find an answer as brilliant as the other times I've posted in the TypeScript issues.


I have a question regarding imports with a computed string.
I noticed they seem to be typed as Promise<any>, but I'd like to know if there's a better way to handle this, so that they could be still typed as Promise<typeof ...>.

I will try to present the issue with minimal and isolated code snippets first and then provide a use case (to give an example).


When I write an import like this:

import(`@/i18n/locales/${language}`)

I have the impression that the type of import(...) becomes Promise<any>.
I deduce this by isolating this import within the assignment of a variable:

const x = () => import(`@/i18n/locales/${'en'}`);
const x: () => Promise<any>

But, when I write:

const x = () => import("@/i18n/locales/en");

Then:

const x: () => Promise<typeof import("/foo/bar/src/i18n/locales/en")>

In the case where it involves string interpolation that is unpredictable, I understand the Promise<any>.
But in some cases, I think that a similar interpolation can be typed and controlled.

For example, using a sum type:

type LanguageFlag = "en" | "fr" | "it";
const LANGUAGES: LanguageFlag[] = ["en", "fr", "it"];

So, I would like my imports to keep the correct typing on the Promise<> type argument.

For instance:

// * ... @/i18n/locales/schema

export default {
  _infos: {
    lng: '',
    label: ''
  },

  navbar: {
    assistance: ''
  },

  auth: {
    login: '',
    logout: '',
    signup: ''
  }
  // * ...
} as const;
//====================
// * ... Types
//====================

//------------------------
// * ... JSON
//------------------------

type JSONPrimitiveLeafs = string | number | boolean | null;
type JSONLeafs = JSONPrimitiveLeafs | JSONPrimitiveLeafs[];

type TypedLeafsJSONData<LeafsTypes extends JSONLeafs, AllowObjArrays extends 'ALLOW_OBJ_ARRAYS' = never> = {
  [_: string]: TypedLeafsJSONData<LeafsTypes> | (AllowObjArrays extends never ? never : TypedLeafsJSONData<LeafsTypes>[]) | LeafsTypes;
};

//------------------------
// * ... Utilitary types
//------------------------

type MakeHomogeneousValuesObjType<Obj extends object, ObjValuesType> = {
  [K in keyof Obj]: Obj[K] extends object ? MakeHomogeneousValuesObjType<Obj[K], ObjValuesType> : ObjValuesType;
};

//------------------------
// * ... Locales
//------------------------

import VOCAB_SCHEMA from '@/i18n/locales/schema';

type LocalesObjKey = LanguageFlag;
type VocabObjValue = string;
type VocabBase = typeof VOCAB_SCHEMA;

type VocabType = MakeHomogeneousValuesObjType<VocabBase, VocabObjValue>;
type NextInternationalMagic = {
  default: VocabType;
};
type NextInternationalLazyLoadFn = () => Promise<NextInternationalMagic>;

type LanguageFlag = "en" | "fr" | "it";

type LocalesObjEntity = [LocalesObjKey, NextInternationalLazyLoadFn];
type LocalesObj = Record<LocalesObjKey, NextInternationalLazyLoadFn>;
type LocalesGetterConfigObjTypeConstraint = Record<LanguageFlag, () => Promise<TypedLeafsJSONData<VocabObjValue>>>;

//====================
// * ... Generator
//====================

const LANGUAGES: LanguageFlag[] = ["en", "fr", "it"];

export const GENERATED_LOCALES_OBJ = Object.fromEntries(
  LANGUAGES.map((language) => [language, () => import(`@/i18n/locales/${language}`)] satisfies LocalesObjEntity)
) as LocalesObj satisfies LocalesGetterConfigObjTypeConstraint;

Here, I am a bit disappointed by this line:

  LANGUAGES.map((language) => [language, () => import(`@/i18n/locales/${language}`)] satisfies LocalesObjEntity)

Since the values of language are predictable:

(parameter) language: "en" | "fr" | "it"

I would have liked having a consistent satisfies LocalesObjEntity.
But since this import is typed as Promise<any>, the satisfies operator is currently completely useless.

If the Promise type argument could be: <typeof .../en ; typeof .../fr ; typeof .../it> the satisfies operator could validate or invalidate the imports.

So, I would like to know: is there a compilation flag I could add, or could I be even stricter and still allowing the possibility of using string interpolations?

Thank you in advance for your responses!

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions