【发布时间】:2021-09-11 20:31:06
【问题描述】:
我正在将一些现有的 JS 代码转换为 TS,我们使用了一种我无法弄清楚如何用 typescript 正确表达的模式:
function getVehicles({
brandFields = false,
ownerFields = false,
maintenanceFields = false
} = {}) {
// building and executing some SQL
}
我们的存储库严重依赖这种模式,我们将获取成本高昂的数据放在一个标志后面,一个函数可以有多个这样的标志。
现在,尝试键入返回值的不同部分有点麻烦,但效果很好:
type Vehicle = { id: dbId, manufactureDate: Date, color: string }
type VehicleBrand = { brandName: string, brandCountry: string }
type VehicleOwner = { owner: Person }
type VehicleMaintenance = { maintenance: { date: Date, place: string, operation: string } [} }
function getVehicles({
brandFields = false,
ownerFields = false,
maintenanceFields = false
} = {}): (Vehicle & VehicleBrand & VehicleOwner & VehicleMaintenance) [] {
// building and executing some SQL
}
但我想让返回类型更精确。 This SO question 建议进行重载,但由于排列的数量,在这种情况下并不实用。
所以我认为留给我的唯一选择是使用泛型和条件类型,类似于:
// With only one parameter for simplicity
function getVehicles<
Brand extends boolean
>({
brandFields: Brand = false
} = {}): (
Vehicle &
(Brand extends true ? VehicleBrand : {})
) [] {
// building and executing some SQL
}
但我还没有找到一种方法来让 typescript 在所有情况下都返回最窄的类型。
getVehicles() // should return Vehicle
getVehicles({ brandFields: false }) // should return Vehicle
getVehicles({ brandFields: true }) // should return Vehicle & VehicleBrand
getVehicles({ brandFields: boolean }) // should return Vehicle & (VehicleBrand | {})
我最接近的是这个签名,但它太松散了:
function getVehicles<
Brand extends boolean
>({
brandFields: Brand | false = false // <-- union to avoid an error ...
} = {}): (
Vehicle &
(Brand extends true ? VehicleBrand : {})
) [] {
// building and executing some SQL
}
getVehicles({ brandFields: true }) // but returns Vehicle & (VehicleBrand | {}) in this case
在当前打字稿的限制下,这甚至可以实现吗?
【问题讨论】:
-
我不确定您需要它的功能有多全面。从纯粹的类型系统的角度来看(而不用担心如何编写实现),this 是否适合您?它是可扩展的(您可以添加更多选项字段)并且它产生我认为合理的输出类型(每个属性组中的属性要么全部存在,要么全部不存在;类型
{}并不意味着缺少键 @987654329 @{foo?: never}的方式)。如果你喜欢这个,我可以写一个答案;否则让我知道我错过了什么。
标签: typescript default-parameters conditional-types