TypeScript实现无限柯里化joinPath,如何返回字面量类型?
为无限柯里化路径拼接函数实现字面量类型返回
原始问题代码
你当前实现的joinPath函数通过无限柯里化拼接路径,但返回类型仅为string,无法保留具体的路径字面量类型:
function joinPath(): string function joinPath(a: string): typeof joinPath function joinPath(a?: string): string | typeof joinPath { if (a === undefined) { return '' } const func = (b?: string) => { if (b === undefined) { return a } return joinPath(`${a}/${b}`) } return func as typeof joinPath } // 类型为 string,而非 'www.github.com' console.log(joinPath('www.github.com')()) // 最终调用类型为 string,而非 'www.github.com/react' console.log(joinPath('www.github.com')('react')())
解决方案:用递归泛型追踪字面量类型
要实现字面量类型的推导,需要定义递归的泛型函数类型,来追踪每一步拼接后的路径字面量:
1. 定义递归柯里化类型
// 递归类型:累积路径为 T,无参调用返回 T,传参则拼接新路径并返回新的柯里化函数 type JoinPathFn<T extends string = ''> = { (): T; <U extends string>(path: U): JoinPathFn<T extends '' ? U : `${T}/${U}`>; };
2. 实现带泛型的柯里化函数
function joinPath(): ''; function joinPath<T extends string>(path: T): JoinPathFn<T>; function joinPath<T extends string>(path?: T): '' | JoinPathFn<T> { if (path === undefined) { return ''; } // 内部函数处理拼接逻辑,通过类型断言匹配泛型类型 const curriedFn = ((nextPath?: string) => { if (nextPath === undefined) { return path; } return joinPath(`${path}/${nextPath}`); }) as unknown as JoinPathFn<T>; return curriedFn; }
3. 测试类型推导
// 类型为 'www.github.com'(字面量类型) const path1 = joinPath('www.github.com')(); // 类型为 'www.github.com/react'(字面量类型) const path2 = joinPath('www.github.com')('react')(); // 类型为 'a/b/c/d'(字面量类型) const path3 = joinPath('a')('b')('c')('d')(); // 类型为 ''(空字符串字面量) const emptyPath = joinPath();
原理说明
JoinPathFn<T>是递归泛型类型,每次传入新路径时,会通过模板字符串类型${T}/${U}拼接出精确的路径字面量,并返回新的柯里化函数类型。- 函数重载明确了不同调用场景的返回类型:无参调用返回空字符串字面量
'',传入路径时返回对应累积路径的柯里化函数。 - 内部函数通过类型断言匹配泛型类型,确保TypeScript能正确推导每一步的字面量类型。
内容的提问来源于stack exchange,提问作者USUNG YANG
相关产品推荐
相关产品推荐

