You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何基于带默认值的可选布尔参数实现条件返回类型

Absolutely, you can achieve this precise type narrowing in TypeScript using generics with conditional types and proper type inference. Here's a clean implementation that handles all your desired scenarios:

type dbId = string | number; // Assuming this is your ID type
type Person = { name: string; id: dbId }; // Defining Person since it's referenced in VehicleOwner

// Your existing core type definitions
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 }[] };

// Generic interface to type the function's options with boolean literals
type GetVehiclesOptions<
  Brand extends boolean = false,
  Owner extends boolean = false,
  Maintenance extends boolean = false
> = {
  brandFields?: Brand;
  ownerFields?: Owner;
  maintenanceFields?: Maintenance;
};

function getVehicles<
  Brand extends boolean = false,
  Owner extends boolean = false,
  Maintenance extends boolean = false
>(
  { brandFields, ownerFields, maintenanceFields }: GetVehiclesOptions<Brand, Owner, Maintenance> = {} as GetVehiclesOptions<Brand, Owner, Maintenance>
): (
  Vehicle 
  & (Brand extends true ? VehicleBrand : {})
  & (Owner extends true ? VehicleOwner : {})
  & (Maintenance extends true ? VehicleMaintenance : {})
)[] {
  // Your SQL construction and execution logic here
  return [] as any; // Placeholder for your actual implementation
}

How this works:

  • Generic Parameter Tracking: We use three constrained generics (Brand, Owner, Maintenance) with default values of false. TypeScript will infer the exact boolean literal (true/false) when you pass a literal value, or the broader boolean type if you pass a variable of type boolean.
  • Conditional Return Type: For each flag, we use a conditional type to either include the related fields (if the flag is true) or an empty object (which has no effect on the type intersection) if it's false or unspecified.
  • Default Parameter Handling: Casting the empty default object to GetVehiclesOptions<Brand, Owner, Maintenance> helps TypeScript correctly infer the default false values when no options are provided.

Validating all your scenarios:

  1. No arguments passed:

    const basicVehicles = getVehicles();
    // Type: Vehicle[] ✅
    
  2. Explicit false for a flag:

    const noBrandVehicles = getVehicles({ brandFields: false });
    // Type: Vehicle[] ✅
    
  3. Explicit true for a flag:

    const brandedVehicles = getVehicles({ brandFields: true });
    // Type: (Vehicle & VehicleBrand)[] ✅
    
  4. Multiple true flags:

    const fullDetailVehicles = getVehicles({ brandFields: true, ownerFields: true, maintenanceFields: true });
    // Type: (Vehicle & VehicleBrand & VehicleOwner & VehicleMaintenance)[] ✅
    
  5. Passing a dynamic boolean variable:

    const shouldIncludeBrand: boolean = Math.random() > 0.5;
    const dynamicVehicles = getVehicles({ brandFields: shouldIncludeBrand });
    // Type: (Vehicle & (VehicleBrand | {}))[] (equivalent to Vehicle[] | (Vehicle & VehicleBrand)[]) ✅
    

Quick notes:

  • The empty object {} in intersections is safe because Vehicle & {} simplifies directly to Vehicle — it doesn’t add or remove any properties.
  • If you prefer the dynamic case to show (Vehicle & Partial<VehicleBrand>)[] instead of the union, you can swap {} with Partial<VehicleBrand> in the conditional types, but both representations are functionally equivalent.

内容的提问来源于stack exchange,提问作者VincentGuinaudeau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 20:12:50