构建在保持类型安全的同时处理多重变化的 Vue 组件可能很棘手。让我们深入研究如何使用变体 Props 模式(VPP) - 一种强大的方法,它使用 TypeScript 的可区分联合类型与 Vue 的组合式 API 来创建真正类型安全的组件变体。
长话短说
Vue 中的 Variant Props 模式将 TypeScript 的可区分联合与 Vue 的 prop 系统相结合,以创建类型安全的组件变体。我们不使用复杂类型实用程序,而是将不兼容的 props 显式标记为 never,以防止在编译时混合 props:
interface BaseProps {
title: string
}
type SuccessProps = BaseProps & {
variant: "success"
message: string
errorCode?: never
}
type ErrorProps = BaseProps & {
variant: "error"
errorCode: string
message?: never
}
type Props = SuccessProps | ErrorProps
该模式提供了编译时安全性、出色的 IDE 支持和可靠的 vue-tsc 兼容性。非常适合需要多个互斥道具组合的组件。
问题:混合 Props 噩梦
想象一下:您正在构建一个需要处理成功和错误状态的通知组件。每个类型都有自己特定的属性:
- 成功需要
message
和duration
- 错误需要
errorCode
和retryable
标志
如果没有适当的安全类型,开发人员可能会意外的混用这些 props
<!-- 🚨 Mixing success and error props -->
<NotificationAlert
variant="primary"
title="Data Saved"
message="Success!"
errorCode="UPLOAD_001"
:duration="5000"
@close="handleClose"
/>
简单的解决方案不起作用
您的第一直觉可能是定义单独的接口:
interface SuccessProps {
title: string
variant: "primary" | "secondary"
message: string
duration: number
}
interface ErrorProps {
title: string
variant: "danger" | "warning"
errorCode: string
retryable: boolean
}
// 🚨 This allows mixing both types!
type Props = SuccessProps & ErrorProps
有什么问题?这种方法允许开发人员同时使用 success 和 error props - 绝对不是我们想要的!
使用 never
TypeScript 提示: never 类型是 TypeScript 中的一种特殊类型,表示从未出现的值。当属性被标记为 never 时,TypeScript 确保永远不能将值分配给该属性。这使得它非常适合创建互斥的 props,因为它可以防止开发人员意外地使用不应该同时存在的 props。 never 类型通常出现在 TypeScript 中的几种场景中:
- 永不返回的函数(抛出错误或具有无限循环)
- switch 语句中的详尽类型检查
- 不可能的类型交叉(例如 string & number)
- 使属性互斥,就像我们在这种模式中所做的那样
使其与当前的 defineProps 实现兼容的主要技巧是使用 never 来显式标记未使用的变体道具。
// Base props shared between variants
interface BaseProps {
title: string
}
// Success variant
type SuccessProps = BaseProps & {
variant: "primary" | "secondary"
message: string
duration: number
// Explicitly mark error props as never
errorCode?: never
retryable?: never
}
// Error variant
type ErrorProps = BaseProps & {
variant: "danger" | "warning"
errorCode: string
retryable: boolean
// Explicitly mark success props as never
message?: never
duration?: never
}
// Final props type - only one variant allowed!
type Props = SuccessProps | ErrorProps
把它们放在一起
这是使用 Variant Props 模式的完整通知组件:
结论
Variant Props Pattern (VPP) 提供了一种构建类型安全的 Vue 组件的强大方法。虽然 Vue 团队正在努力改进 vuejs/core#8952 中对受歧视联合体的本机支持,但这种模式今天提供了一个实用的解决方案:
不幸的是,目前不起作用的是使用像 Xor 这样的辅助实用程序类型,这样我们就不必手动将未使用的变体属性标记为从不。当你这样做时,你会从 vue-tsc 收到错误。
Xor 等辅助类型的示例:
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never }
type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U
// Success notification properties
interface SuccessProps {
title: string
variant: "primary" | "secondary"
message: string
duration: number
}
// Error notification properties
interface ErrorProps {
title: string
variant: "danger" | "warning"
errorCode: string
retryable: boolean
}
// Final props type - only one variant allowed! ✨
type Props = XOR<SuccessProps, ErrorProps>
评论区
评论加载中...