Search code examples
typescripttypesreturnoverloadingtype-inference

How to infer or to deduce the return type from an argument in typescript


I have a type that is deductible from one field of the object.

And I wrote this function that takes the zoneType and returns the associate zone.

But at the end, and on each call I get : MyType1 | MyType2 | MyType3 and not the associated type. This type is actually deductible from the argument, since the switch case can easily find the type.

We are talking about geojson btw.

I was wondering trying overloading, but typescript does not allow having duplicated functions. Also, infer, but it looks "huge" for something that can be guessed by TS itself?

I will appreciate your help.

Thanks !

type MyType1 = "geojson.Feature<geojson.MultiPolygon>";
type MyType2 = "geojson.Feature<geojson.Polygon>";
type MyType3 = "geojson.FeatureCollection<geojson.Polygon>";

export type ZoneGeojson = MyType1 | MyType2 | MyType3;

type InternalZone = {
  id: number;
  zone: ZoneGeojson | null;
  zone_type: ZoneZoneType;
};

export interface Zone1 extends InternalZone {
  zone_type: ZoneZoneType.Type1 | ZoneZoneType.Type2;
  zone: MyType1 | null;
}

export interface Zone2 extends InternalZone {
  zone_type: ZoneZoneType.Type3;
  zone: MyType1 | null;
}

export interface Zone3 extends InternalZone {
  zone_type: ZoneZoneType.Type4;
  zone: MyType1 | null;
}
export const exportZone = (
  zoneType: ZoneZoneType,
  zone: FeatureCollection<Polygon>,
) => {
  switch (zoneType) {
    case ZoneZoneType.Type1:
    case ZoneZoneType.Type2:
      return zone1.zoneGeojsonFactory.exportZoneEdition(zone);
    case ZoneZoneType.Type3:
      return zone2.zoneGeojsonFactory.exportZoneEdition(zone);
    case ZoneZoneType.Type4:
      return zone3.zoneGeojsonFactory.exportZoneEdition(zone);
    default:
      // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
      throw new Error(`Unknown zone type ${zoneType ?? '[undefined]'}`);
  }
};

Solution

  • This is some specific case of return type based on parameter

    Applied to your case :

    type ObjectType<T> = 
      T extends ZoneZoneType.Type1 ? Zone1 :
      T extends ZoneZoneType.Type2 ? Zone1 :
      T extends ZoneZoneType.Type3 ? Zone2 :
      T extends ZoneZoneType.Type4 ? Zone3 :
    never;
    
    export const exportZoneEditionGeojson = <T extends ZoneZoneType>(
      zoneType: T,
      zoneEditionGeojson: FeatureCollection<Polygon>,
    ): ObjectType<T> => {
      switch (zoneType) {
        case ZoneZoneType.Type1:
        case ZoneZoneType.Type2:
          return zone1.zoneGeojsonFactory
            .exportZoneEdition(zoneEditionGeojson) as ObjectType<T>;
        case ZoneZoneType.Type3:
          return zone2.zoneGeojsonFactory
            .exportZoneEdition(zoneEditionGeojson) as ObjectType<T>;
        case ZoneZoneType.Type4:
          return zone3.zoneGeojsonFactory
            .exportZoneEdition(zoneEditionGeojson) as ObjectType<T>;
        default:
          // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
          throw new Error(`Unknown zone type ${zoneType}`);
      }
    };