網站搜尋

如何定義這樣一個 TypeScript 泛型,遍歷路由配置樹並累積路徑


我有

type Route = {
  path: string
  children?: readonly Route[]
}

const root = {
  path: "a",
  children: [
    { path: "b" },
    { path: "c", children: [{ path: "d", children: [{ path: "e" }] }] }
  ],
} as const satisfies Route;

我想定義一個 TypeScript 泛型 Convert,使得 Convert<typeof root> 的輸出為字串列舉 'a' | 'a/b' | 'a/c' | 'a/c/d' | 'a/c/d/e'

type Convert<T extends Route> = ?

我試了類似的東西

type Convert<T extends Route, Prefix extends string = ''> = 
`${Prefix}/${T['path']}` | (
    T["children"] extends readonly Route[]
      ? {
        [K in number]: Convert<T['children'][K], `${Prefix}/${T['path']}`>;
      }[number]
      : never)

但是 Output="/a" | "/a/b" | "/a/c" 不夠深入。

https://www.typescriptlang.org/play?#code/C4TwDgpgBASg9gV2NAvFA3gWAFBSmAQ2AAsAuKAZ2ACcBLAOwHMc8BjY2gGwBNqJ6A-OT4FucepxCxEyANoBdHAF8cOVuKpRqcOMChosufETJQARATMAaFlHZde-crNt50xkuTMAjM1CU2Rm4epmas1nYcPHz0zu6EnubcEfbRTlCy8SZeEH5K8v4FKkbygUpQBBR2GnoURLQUAGa0EFXwSBAA3KrYoJBQAKIAHpCsemgA5AQTUAA+UFMA9N4z80vei6yrCwTLm4vc2+v73IsQEz190ADC4gBuENTAADwAKlAQQ8j03G0yEFYoAAFPjNIYfL78X6UGgMRj6BYTAB8CJwAAMACToEEQMFKRZY16yCYJYgTeRKNFzKAAClcUCJYSijnoZgKn2+0JEYgkUnackUQTwUAEGHpwoyAGkoAwoPQEABbbyPeTkW70B5PN7E1Is8mySWlKCY7Gg2hDfGE4mk8mUpHdIV4JSyeVKlXiqDkegQTUASku4Ggw0IP2eAA0UWgwxDOVUEPQANb0OAAd3oIqg0a9PseDquUAA8kgwEgEcGCKH1ZqXlc4I0tDpgEikUA

感謝你的幫助!

答案#1

毫無疑問,有許多方式可以做到這一點。這裡有一個方法:

type Convert<T extends Route> =
  T['path'] | (
    T extends { children: readonly (infer R extends Route)[] } ?
    `${T['path']}/${Convert<R>}` : never
  );

我在那裡所做的只是輸出 path 屬性,並遞歸抓取 R,也就是 children 屬性中的 Route 聯集,然後應用 Convert<R>… 並將其附加到 path 屬性後面,並加上一個斜線。

讓我們確保它能正常運作:

type Expect = 'a' | 'a/b' | 'a/b/c' | 'a/b/c/d' | 'a/b/c/d/e'
type Actual = Convert<typeof root>;
// type Actual = "a" | "a/b" | "a/c" | "a/c/d" | "a/c/d/e"

看起來不錯。

程式碼的 Playground 連結