TypeScript中如何指定函数接收并返回特定字符串?代码疑问解析
现有代码的工作原理拆解
逐行解析代码逻辑:
const Locales = { en_gb: 'en-gb', en_us: 'en-us' } as const
加as const后,TypeScript会将这个对象锁定为只读字面量类型,不会把属性值宽泛推断为string,而是保留具体的字符串值('en-gb'、'en-us'),同时属性键也固定为'en_gb'、'en_us'。type ApiLocales = typeof Locales[keyof typeof Locales]keyof typeof Locales获取Locales的所有键(联合类型'en_gb' | 'en_us'),再用这个键索引typeof Locales,得到对应的值类型——也就是API端使用的语言码:'en-gb' | 'en-us'。type DatabaseLocales = keyof typeof Locales
直接取Locales的键类型,即数据库端接受的语言码:'en_gb' | 'en_us'。函数定义的问题
你写的返回类型DatabaseLocales[ApiLocales]是错误的:DatabaseLocales是联合类型,不能用另一个联合类型去索引它,TypeScript会直接报错。虽然函数内部的对象映射逻辑在运行时可行,但类型注解不合法,无法起到类型校验作用。
修正后的可行代码
通过创建反向映射对象并推导类型,让类型注解和逻辑统一:
const Locales = { en_gb: 'en-gb', en_us: 'en-us', } as const; // API语言码类型 type ApiLocales = typeof Locales[keyof typeof Locales]; // 数据库语言码类型 type DatabaseLocales = keyof typeof Locales; // 创建反向映射对象,让TypeScript自动推导其类型 const ApiToDbMap = { 'en-gb': 'en_gb', 'en-us': 'en_us', } as const; // 参数和返回类型精准对应,编译时自动校验 function databaseLanguageCodeFromString(languageString: ApiLocales): typeof ApiToDbMap[ApiLocales] { return ApiToDbMap[languageString]; }
编译时+运行时双重校验(提前拦截问题)
为了在数据提交到数据库前提前发现问题,可做两层校验:
1. 编译时校验
上面的代码已通过TypeScript类型系统确保:
- 函数只能接收
ApiLocales范围内的字符串(传'zh-cn'这类非法值会直接编译报错) - 返回值必然是
DatabaseLocales范围内的字符串,不会出现数据库不接受的值
2. 运行时兜底校验
如果有非预期的运行时输入(比如API返回未定义的语言码),可加兜底逻辑抛出错误:
function databaseLanguageCodeFromString(languageString: string): DatabaseLocales { const dbCode = ApiToDbMap[languageString as ApiLocales]; if (!dbCode) { throw new Error(`无效的API语言码:${languageString},无法转换为数据库支持的格式`); } return dbCode; }
额外优化:避免重复定义映射
直接从Locales对象生成反向映射,后续新增语言码只需修改Locales,减少重复代码和出错概率:
const Locales = { en_gb: 'en-gb', en_us: 'en-us', } as const; type ApiLocales = typeof Locales[keyof typeof Locales]; type DatabaseLocales = keyof typeof Locales; // 从Locales自动生成反向映射 const ApiToDbMap = Object.fromEntries( Object.entries(Locales).map(([dbCode, apiCode]) => [apiCode, dbCode]) ) as { [K in ApiLocales]: DatabaseLocales }; function databaseLanguageCodeFromString(languageString: ApiLocales): DatabaseLocales { return ApiToDbMap[languageString]; }
内容的提问来源于stack exchange,提问作者Ben Woodward

