TypeScript异步加载JSON:如何避免代码重复并获取正确类型形状?
TypeScript异步加载JSON文件获取正确类型的优化方案
问题场景
当通过异步函数动态加载不同语言的JSON文件时,所有JSON结构完全一致,但直接通过Awaited<ReturnType<typeof loadMessages>>推导类型时,foo字段会被解析为any:
export type AppMessages = Awaited<ReturnType<typeof loadMessages>>; export type Locale = "en" | "fr" | "es"; export const loadMessages = async (locale: Locale) => ({ foo: locale === 'en' ? (await import('../../messages/en/foo.json')).default : (await import(`../../messages/${locale}/foo.json`)).default });
推导结果:
type AppMessages = { foo: any; }
之前的解决方案虽能得到正确类型,但存在大量重复代码,现需要一种无需手动定义AppMessages、无代码冗余的方案。
优化方案
方案1:基于默认JSON文件提取基准类型
直接从默认语言的JSON文件中提取类型,作为所有动态加载JSON的类型基准,无需重复定义结构:
// 导入默认语言的JSON,仅用于提取类型(编译时不会引入冗余代码) import enFoo from '../../messages/en/foo.json'; type FooMessages = typeof enFoo; export type Locale = "en" | "fr" | "es"; // 明确返回值类型,对齐基准类型 export const loadMessages = async (locale: Locale): Promise<{ foo: FooMessages }> => ({ foo: (await import(`../../messages/${locale}/foo.json`)).default }); // 自动推导正确的AppMessages类型,无需手动编写结构 export type AppMessages = Awaited<ReturnType<typeof loadMessages>>;
方案2:复用默认加载函数提取类型
利用默认语言的加载函数同时实现逻辑复用和类型提取,进一步简化代码:
export type Locale = "en" | "fr" | "es"; // 默认语言加载函数,既用于逻辑实现,也用于提取类型 const loadEnMessages = async () => ({ foo: (await import('../../messages/en/foo.json')).default }); // 从默认加载函数提取基准类型 type BaseMessages = Awaited<ReturnType<typeof loadEnMessages>>; // 通用加载函数,直接复用基准类型 export const loadMessages = async (locale: Locale): Promise<BaseMessages> => { if (locale === 'en') { return loadEnMessages(); } return { foo: (await import(`../../messages/${locale}/foo.json`)).default }; }; // AppMessages直接复用基准类型,无需额外推导 export type AppMessages = BaseMessages;
方案说明
两种方案均利用TypeScript的类型推导能力,通过typeof、ReturnType、Awaited等工具类型从已有资源(JSON文件或加载函数)中提取类型,确保动态加载的JSON类型与基准类型一致,同时完全避免了代码重复和手动定义类型结构的繁琐。
内容的提问来源于stack exchange,提问作者Yanick Rochon
相关产品推荐
相关产品推荐

