路径的推理相当复杂,特别是对于静态网站生成器而言,它们的来源可能多种多样。
一个内容片段的完整文件路径?这也是一种路径。内容的短链接(slug)呢?又是另一种路径。
如果将这些都简单地定义为 string
类型就草草了事会显得很愚蠢,因为在实际操作中很容易将不同类型的路径混淆。遗憾的是,TypeScript 的类型别名并不支持名义类型系统,这意味着即使你为服务端 slug 或客户端 slug 创建了自定义类型,仍然可能意外地将两者互相赋值,而 TypeScript 无法捕获这种错误。
幸运的是,我们可以通过类型烙印来模拟名义类型系统。
// instead of
type FullSlug = string
// we do
type FullSlug = string & { __brand: "full" }
// that way, the following will fail typechecking
const slug: FullSlug = "some random string"
While this prevents most typing mistakes within our nominal typing system (e.g. mistaking a server slug for a client slug), it doesn’t prevent us from accidentally mistaking a string for a client slug when we forcibly cast it.
Thus, we still need to be careful when casting from a string to one of these nominal types in the ‘entrypoints’, illustrated with hexagon shapes in the diagram below.
The following diagram draws the relationships between all the path sources, nominal path types, and what functions in quartz/path.ts
convert between them.
graph LR Browser{{Browser}} --> Window{{Body}} & LinkElement{{Link Element}} Window --"getFullSlug()"--> FullSlug[Full Slug] LinkElement --".href"--> Relative[Relative URL] FullSlug --"simplifySlug()" --> SimpleSlug[Simple Slug] SimpleSlug --"pathToRoot()"--> Relative SimpleSlug --"resolveRelative()" --> Relative MD{{Markdown File}} --> FilePath{{File Path}} & Links[Markdown links] Links --"transformLink()"--> Relative FilePath --"slugifyFilePath()"--> FullSlug[Full Slug] style FullSlug stroke-width:4px
以下是主要的 slug 类型及其路径描述的简要说明:
FilePath
: 表示磁盘上真实文件的路径。不能是相对路径且必须包含文件扩展名。FullSlug
: 不能是相对路径且不能包含开头或结尾的斜杠。其最后一段可以是index
。在大多数情况下应优先使用此类型,因为它是对 slug 最通用的解释。SimpleSlug
: 不能是相对路径且不应以/index
结尾或包含文件扩展名。但_可以_包含尾部斜杠来表示文件夹路径。RelativeURL
: 必须以.
或..
开头表示相对路径。不应以/index
结尾或包含文件扩展名,但可以包含尾部斜杠。
要更清楚地了解这些类型之间的关系,请查看 quartz/util/path.test.ts
中的路径测试。