求助!在 Vue 3 中通过 import.meta.glob 载入的文件,我应该如何进行字面量类型推断?

以下是代码,关于 naive-ui 可以删除, Record<string, { default: GlobalThemeOverrides } | unknown> 替换为 any

import type { GlobalThemeOverrides } from 'naive-ui'

// 根据 themes 文件夹内的输出结合 import.meta.glob 推断类型是 Record<string, { default: GlobalThemeOverrides }>
const modules = import.meta.glob('./themes/*.ts', { eager: true })

function formatImports(
  modules: Record<string, { default: GlobalThemeOverrides } | unknown>,
  result: ObjectAny
) {
  Object.keys(modules).forEach((key) => {
    const defaultModule = (modules[key] as ObjectAny).default
    if (!defaultModule) return
    const name = key.split('/').pop()?.replace('.ts', '')
    result[name as string] = defaultModule
  })
  return result
}

// themes result
// result: { gray: {}, slate: {} }
// 需要进行类型生成
// type ThemeName = 'gray' | 'slate'
export const themes = formatImports(modules, {})
export const themeNames = Object.keys(themes)
export type ThemeName = (typeof themeNames)[number]
// type UnionOfArrayElements<T extends readonly string[]> = T[number]
// export type ThemeName = UnionOfArrayElements<typeof themeNames>

// 使用过以下方法进行类型推断,但都不能正确的推断字面量类型,似乎字面量类型推断只能是定义的数据而非生成的数据
// 如果你遇到了这个问题并且有解决办法,请求你能够像我分享相关解决办法,非常感谢!
//
// 1. as const
// export const themeNames = Object.keys(themes) as const
// export type ThemeName = (typeof themeNames)[number]
//
// 该方法会出现 TS 错误,因为 as const 不能被这样使用
// Error: 'const' 断言只能作用于枚举成员、字符串、数字、布尔值、数组或对象字面量。
//
// =========================================================================
//
// 2. 辅助类型推断
// type UnionOfArrayElements<T extends readonly string[]> = T[number]
// export type ThemeName = UnionOfArrayElements<(typeof themes)[keyof typeof themes]>
//
// 最终推断出来的是 'gray' 的类型(string),而不是将 'gray' 当成类型
阅读 1.1k
2 个回答

ts 不负责运行时,你的 modules 只有运行之后才知道有什么文件

import type { GlobalThemeOverrides } from 'naive-ui';

// 使用 import.meta.glob 导入所有主题模块
const modules = import.meta.glob('./themes/*.ts', { eager: true });

// 定义一个接口表示模块的类型
interface ThemeModule {
  default: GlobalThemeOverrides;
}

// 强制类型转换,将 modules 的类型指定为 Record<string, ThemeModule>
const typedModules = modules as Record<string, ThemeModule>;

// 定义一个类型帮助函数,用于推断主题名称
function formatImports(
  modules: Record<string, ThemeModule>,
  result: Record<string, GlobalThemeOverrides>
) {
  Object.keys(modules).forEach((key) => {
    const defaultModule = modules[key].default;
    const name = key.split('/').pop()?.replace('.ts', '');
    if (name) {
      result[name] = defaultModule;
    }
  });
  return result;
}

// 生成 themes 对象
const themes = formatImports(typedModules, {});

// 获取主题名称的数组,并使用 as const 断言以确保是字面量类型
const themeNames = Object.keys(themes) as Array<keyof typeof themes>;

// 导出主题名称类型
export type ThemeName = (typeof themeNames)[number];

// 导出 themes 对象和 themeNames 数组
export { themes, themeNames };
推荐问题
logo
Microsoft
子站问答
访问
宣传栏