如何解决打字稿 - 如何根据具有默认值的可选布尔参数执行条件返回类型
我正在将一些现有的 JS 代码转换为 TS,我们使用了一种模式,我无法弄清楚如何用打字稿正确表达:
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,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
在当前的打字稿限制下,这甚至可以实现吗?
解决方法
您可以使用 conditional types 实现这一点,如下所示:
type Vehicle<O extends OptionsFlags> = VehicleBase &
(O extends { brandFields: true }
? VehicleBrand
: O extends { brandFields: false | undefined }
? {}
: VehicleBrand | {}) &
(O extends { ownerFields: true }
? VehicleOwner
: O extends { ownerFields: false | undefined }
? {}
: VehicleOwner | {}) &
(O extends { maintenanceFields: true }
? VehicleMaintenance
: O extends { maintenanceFields: false | undefined }
? {}
: VehicleMaintenance | {});
interface OptionsFlags {
brandFields?: boolean;
ownerFields?: boolean;
maintenanceFields?: boolean;
}
interface VehicleBase {
id: dbId;
manufactureDate: Date;
color: string;
}
interface VehicleBrand {
brandName: string;
brandCountry: string;
}
interface VehicleOwner {
owner: Person;
}
interface VehicleMaintenance {
maintenance: { date: Date; place: string; operation: string }[];
}
function getVehicles<O extends OptionsFlags>({
brandFields = false,ownerFields = false,maintenanceFields = false,}: O = {} as O): Vehicle<O>[] {
// ...
}
getVehicles({ brandFields: true }) // return type is Vehicle<{ brandFields: true }>[]
不过……
根据您如何使用它,您可能会发现像这样定义 Vehicle
类型更有帮助,它允许每个 Vehicle
子类型解析为可能具有可选属性的单个接口:
type Vehicle<O extends OptionsFlags> = VehicleBase &
(O extends { brandFields: true }
? VehicleBrand
: O extends { brandFields: false | undefined }
? {}
: Partial<VehicleBrand>) &
(O extends { ownerFields: true }
? VehicleOwner
: O extends { ownerFields: false | undefined }
? {}
: Partial<VehicleOwner>) &
(O extends { maintenanceFields: true }
? VehicleMaintenance
: O extends { maintenanceFields: false | undefined }
? {}
: Partial<VehicleMaintenance>);
Vehicle<{ brandFields: boolean }>
则相当于:
{
id: dbId;
manufactureDate: Date;
color: string;
brandName?: string;
brandCountry?: string;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。