Geocodio / geocodio-library-node

geocod.io Node library
MIT License
15 stars 6 forks source link

Adding Typescript Declarations #17

Open defensadev opened 3 years ago

defensadev commented 3 years ago

Hello, I really love geocodio, it's a great service and the node library work well.

I found it was missing a @types/geocodio-library-node module. So I made some quick type declarations for my work as I had to build it around a pre existing project.

I just wanted to leave it here for improvement and to help anybody out. It might not be exact and have some errors, I put it together rather quickly by just looking at the docs and testing the responses.


type GeocodeProps = string | Array<string>;

interface GeocodedAddress {
  address_components: {
    number?: string;
    predirectional?: string;
    street?: string;
    suffix?: string;
    formatted_street?: string;
    secondaryunit?: string;
    secondarynumber?: string;
    city: string;
    county?: string;
    state: string;
    zip: string;
    country?: string;
  };
  formatted_address: string;
  location: { lat: number; lng: number };
  accuracy?: number;
  accuracy_type?: string;
  source?: string;
}

interface GeocodedResponse {
  results: [GeocodedAddress];
}

interface GeocodedArrayResponse {
  results: Array<{
    query: string;
    response: {
      input: any;
      results: [GeocodedAddress];
    };
  }>;
}

declare module "geocodio-library-node" {
  export default class Geocodio {
    constructor(apiKey: string);

    geocode(
      x: GeocodeProps,
      y: Array<any>,
      z: number
    ): GeocodeProps extends string
      ? Promise<GeocodedResponse>
      : Promise<GeocodedArrayResponse>;
  }
}
Theauxm commented 2 years ago
declare module "geocodio-library-node" {
  export interface AddressParts {
    country?: string;
    street?: string;
    city?: string;
    state?: string;
    postal_code?: string;
  }

  export type AddressMap = {
    [key: string]: string;
  };

  export type AddressComponents = {
    number?: string;
    predirectional?: string;
    street?: string;
    suffix?: string;
    formatted_street?: string;
    secondaryunit?: string;
    secondarynumber?: string;
    city: string;
    county?: string;
    state: string;
    zip: string;
    country?: string;
  };

  export interface GeocodedAddress {
    address_components: AddressComponents;
    formatted_address: string;
    location: { lat: number; lng: number };
    accuracy?: number;
    accuracy_type?: string;
    source?: string;
    fields?: { [key: string]: string };
  }

  export interface GeocodedResponse {
    input: {
      address_components: AddressComponents;
      formatted_address: string;
    };
    results: [GeocodedAddress];
  }

  export interface GeocodedArrayResponse {
    results: Array<{
      query: string;
      response: GeocodedResponse;
      results: [GeocodedAddress];
    }>;
  }

  export type AddressParam =
    | string
    | string[]
    | AddressParts
    | AddressParts[]
    | AddressMap;

  export type CoordinatesParam = string | string[] | [number, number];

  export default class Geocodio {
    constructor(apiKey?: string);

    geocode(
      address: AddressParam,
      options?: string[],
      limit?: number
    ): Promise<GeocodedResponse | GeocodedArrayResponse>;

    reverse(
      coordinates: CoordinatesParam,
      options?: string[],
      limit?: number
    ): Promise<GeocodedResponse | GeocodedArrayResponse>;
  }
}
defensadev commented 2 years ago

Brilliant! Haven't worked on this in a while, I just use my type defs as they satisfy what I'm working on. Good to see there's a complete list here. I'll put them in my .d.ts right now.

Theauxm commented 2 years ago

I reworked it slightly as I couldn't figure out how to get a Simple response to be returned from the library. Apologies about the non-usage of ternary types, couldn't figure that one out!

Sparticuz commented 2 years ago

I've added error and _warnings to @Theauxm's types. Also my linter wants things alphabetized, so I did that too

declare module "geocodio-library-node" {
  export interface AddressParts {
    city?: string;
    country?: string;
    postal_code?: string;
    state?: string;
    street?: string;
  }

  export type AddressMap = {
    [key: string]: string;
  };

  export type AddressComponents = {
    city: string;
    country?: string;
    county?: string;
    formatted_street?: string;
    number?: string;
    predirectional?: string;
    secondarynumber?: string;
    secondaryunit?: string;
    state: string;
    street?: string;
    suffix?: string;
    zip: string;
  };

  export interface GeocodedAddress {
    _warnings?: string[];
    accuracy?: number;
    accuracy_type?: string;
    address_components: AddressComponents;
    fields?: { [key: string]: string };
    formatted_address: string;
    location: { lat: number; lng: number };
    source?: string;
  }

  export interface GeocodedResponse {
    _warnings?: string[];
    input: {
      address_components: AddressComponents;
      formatted_address: string;
    };
    results: [GeocodedAddress];
  }

  export interface GeocodedArrayResponse {
    results: Array<{
      query: string;
      response: GeocodedResponse;
      results: [GeocodedAddress];
    }>;
  }

  export interface GeocodedErrorResponse {
    error: string;
  }

  export type AddressParam =
    | string
    | string[]
    | AddressParts
    | AddressParts[]
    | AddressMap;

  export type CoordinatesParam = string | string[] | [number, number];

  export default class Geocodio {
    constructor(apiKey?: string);

    geocode(
      address: AddressParam,
      options?: string[],
      limit?: number
    ): Promise<
      GeocodedResponse | GeocodedArrayResponse | GeocodedErrorResponse
    >;

    reverse(
      coordinates: CoordinatesParam,
      options?: string[],
      limit?: number
    ): Promise<
      GeocodedResponse | GeocodedArrayResponse | GeocodedErrorResponse
    >;
  }
}

And some type guards

function isGeocodedError(
  response: GeocodedArrayResponse | GeocodedResponse | GeocodedErrorResponse
): response is GeocodedErrorResponse {
  return (response as GeocodedErrorResponse).error !== undefined;
}

function isGeocodedArrayResponse(
  response: GeocodedArrayResponse | GeocodedResponse | GeocodedErrorResponse
): response is GeocodedArrayResponse {
  return (response as GeocodedArrayResponse).results[0].query !== undefined;
}
TheSecurityDev commented 1 year ago

Extended from @Sparticuz

declare module "geocodio-library-node" {
  export interface AddressParts {
    city?: string;
    country?: string;
    postal_code?: string;
    state?: string;
    street?: string;
  }

  export type AddressMap = {
    [key: string]: string;
  };

  export interface AddressComponents {
    city: string;
    country?: string;
    county?: string;
    formatted_street?: string;
    number?: string;
    predirectional?: string;
    state: string;
    street?: string;
    suffix?: string;
    zip: string;
  }

  export interface InputAddressComponents extends AddressComponents {
    secondarynumber?: string;
    secondaryunit?: string;
  }

  export type ForwardGeocodeAccuracyType =
    | "rooftop"
    | "point"
    | "range_interpolation"
    | "nearest_rooftop_match"
    | "intersection"
    | "street_center"
    | "place"
    | "county"
    | "state";

  export type ReverseGeocodeAccuracyType = "rooftop" | "nearest_street" | "nearest_place";

  export type GeocodeAccuracyType = ForwardGeocodeAccuracyType | ReverseGeocodeAccuracyType;

  export interface GeocodedAddress<T = GeocodeAccuracyType> {
    _warnings?: string[];
    accuracy?: number;
    accuracy_type?: T;
    address_components: AddressComponents;
    fields?: { [key: string]: string };
    formatted_address: string;
    location: { lat: number; lng: number };
    source?: string;
  }

  export interface GeocodedResponse<T = GeocodeAccuracyType> {
    _warnings?: string[];
    input: {
      address_components: InputAddressComponents;
      formatted_address: string;
    };
    results: GeocodedAddress<T>[];
  }

  export interface GeocodedArrayResponse<T = GeocodeAccuracyType> {
    results: Array<{
      query: string;
      response: GeocodedResponse<T>;
      results: GeocodedAddress<T>[];
    }>;
  }

  export interface GeocodedErrorResponse {
    error: string;
  }

  export type AddressParam = string | string[] | AddressParts | AddressParts[] | AddressMap;

  export type CoordinatesParam = string | string[] | [number, number];

  export default class Geocodio {
    constructor(apiKey?: string);

    geocode(
      address: AddressParam,
      options?: string[],
      limit?: number
    ): Promise<
      | GeocodedResponse<ForwardGeocodeAccuracyType>
      | GeocodedArrayResponse<ForwardGeocodeAccuracyType>
      | GeocodedErrorResponse
    >;

    reverse(
      coordinates: CoordinatesParam,
      options?: string[],
      limit?: number
    ): Promise<
      | GeocodedResponse<ReverseGeocodeAccuracyType>
      | GeocodedArrayResponse<ReverseGeocodeAccuracyType>
      | GeocodedErrorResponse
    >;
  }
}
MiniCodeMonkey commented 8 months ago

@Sparticuz @Theauxm @TheSecurityDev @defensadev Thanks for your collaboration on putting together the Typescript declarations. We would be happy to accept a PR to add these to the library if you'd like. Thanks!

cameron-hicks commented 1 week ago

TheSecurityDev's types are great, using them in my project with some adjustments. I'm also using the zip4 and census field appends, so I've added types for those fields:

// modified from TheSecurityDev's types
interface GeocodedAddress<T = GeocodeAccuracyType> {
    _warnings?: string[];
    accuracy?: number;
    accuracy_type?: T;
    address_components: AddressComponents;
    fields?: Fields;
    formatted_address: string;
    location: { lat: number; lng: number };
    source?: string;
}

interface Fields {
    zip4?: Zip4;
    census?: {
        [year: string]: Census;
    };
    [key: string]: unknown;
}

interface Zip4 {
    record_type: {
        code: string;
        description: string;
    };
    residential: boolean;
    carrier_route: {
        id: string;
        description: string;
    };
    building_or_firm_name: string;
    plus4: string[];
    zip9: string[];
    government_building: unknown | null;
    facility_code: {
        code: string;
        description: string;
    };
    city_delivery: boolean;
    valid_delivery_area: boolean;
    exact_match: boolean;
}

interface Census {
    census_year: number;
    state_fips: string;
    county_fips: string;
    tract_code: string;
    block_code: string;
    block_group: string;
    full_fips: string;
    place: {
        name: string;
        fips: string;
    } | null;
    metro_micro_statistical_area: {
        name: string;
        area_code: string;
        type: string;
    } | null;
    combined_statistical_area: {
        name: string;
        area_code: string;
    } | null;
    metropolitan_division: {
        name: string;
        area_code: string;
    } | null;
    county_subdivision: {
        name: string;
        fips: string;
        fips_class: {
            class_code: string;
            description: string;
        };
    };
    source: string;
}