如何为jsonCasingParser函数实现参数依赖的返回类型收窄
实现带自动类型收窄的对象键命名格式转换函数
首先补充原代码中缺失的正则表达式常量(否则无法正常运行):
const CAMEL_CASE_PATTERN = /([a-z])([A-Z])/g; const SNAKE_CASE_PATTERN = /([a-z])_([a-z])/g; const JSON_VARIABLE_PATTERN = /"(.*?)"/g; // 匹配JSON字符串中的键名
定义键名类型转换工具
要实现返回类型自动收窄,需要先定义TypeScript工具类型来处理键名的类型转换:
// 将蛇形键转换为驼峰键的类型工具 type SnakeToCamelCase<S extends string> = S extends `${infer Prefix}_${infer First}${infer Suffix}` ? `${Prefix}${Uppercase<First>}${SnakeToCamelCase<Suffix>}` : S; // 将驼峰键转换为蛇形键的类型工具 type CamelToSnakeCase<S extends string> = S extends `${infer Prefix}${infer First}${infer Suffix}` ? `${Prefix}${Lowercase<First>}${First extends Uppercase<First> ? '_' : ''}${CamelToSnakeCase<Suffix>}` : S; // 递归转换对象所有层级键的类型 type TransformKeys<T, Transform extends (key: string) => string> = T extends object ? { [K in keyof T as Transform extends (key: infer U) => infer R ? U extends K ? R : never : never]: T[K] extends object ? TransformKeys<T[K], Transform> : T[K] } : T;
修改核心函数实现(带类型收窄)
通过函数重载+泛型条件类型,让jsonCasingParser根据输入自动匹配返回类型:
export enum CasingPattern { SNAKE, CAMEL, } export const toSnakeCase = (camelCased: string) => { const replaced = camelCased.replace( new RegExp(CAMEL_CASE_PATTERN, 'g'), (match) => { const arr = match.split(''); const last = arr.pop(); const parsed = arr.join('') + '_' + last?.toLowerCase(); return parsed; }, ); return replaced; }; export const toCamelCase = (snakeCased: string) => { const replaced = snakeCased.replace( new RegExp(SNAKE_CASE_PATTERN, 'g'), (match) => { const [lastLetter, firstLetter] = match.split('_'); const capitalize = firstLetter.toUpperCase(); return lastLetter + capitalize; }, ); return replaced; }; // 函数重载:明确不同输入对应的返回类型 export function jsonCasingParser(jsonPayload: string, targetPattern: CasingPattern): string; export function jsonCasingParser<T extends object>(jsonPayload: T, targetPattern: CasingPattern.CAMEL): TransformKeys<T, typeof toCamelCase>; export function jsonCasingParser<T extends object>(jsonPayload: T, targetPattern: CasingPattern.SNAKE): TransformKeys<T, typeof toSnakeCase>; // 函数实现 export function jsonCasingParser( jsonPayload: unknown, targetPattern: CasingPattern, ): unknown { const isString = typeof jsonPayload === 'string'; const stringified = isString ? jsonPayload : JSON.stringify(jsonPayload); const replaced = stringified.replace( new RegExp(JSON_VARIABLE_PATTERN, 'g'), (match) => { // 先去掉键名的引号再处理,避免引号被误转换 const key = match.slice(1, -1); let transformedKey = key; if (targetPattern === CasingPattern.SNAKE) { transformedKey = toSnakeCase(key); } else if (targetPattern === CasingPattern.CAMEL) { transformedKey = toCamelCase(key); } return `"${transformedKey}"`; }, ); if (isString) { return replaced; } return JSON.parse(replaced); }
类型验证示例
// 返回 string 类型 const strResult = jsonCasingParser('{ "propOne": "one" }', CasingPattern.CAMEL); // 返回 { prop_one: string } 类型 const snakeResult = jsonCasingParser({ propOne: "one" }, CasingPattern.SNAKE); // 返回 { propOne: string } 类型 const camelResult = jsonCasingParser({ prop_one: "one" }, CasingPattern.CAMEL);
关键说明
- 工具类型:通过
SnakeToCamelCase和CamelToSnakeCase实现字符串键名的类型映射,TransformKeys递归处理嵌套对象的键类型转换。 - 函数重载:明确不同输入参数组合对应的返回类型,让TypeScript自动收窄返回值的类型推断结果。
- 实现调整:优化了正则替换逻辑,先剥离键名的引号再处理,避免引号被错误转换。
内容的提问来源于stack exchange,提问作者Cristian Flórez
相关产品推荐
相关产品推荐

