Skip to content

type does not allow custom fields #662

@y-nk

Description

@y-nk

no matter v1 or v2, the payload types defined don't allow for custom fields. it forces us to cast as any to be able to use it, making the value of typescript void. custom fields are a huge part of pipedrive so it's really inconvenient that we can't use them properly.

In hope for a faster path of remediation, I've doodled some type utilities which may achieve custom fields at quite low cost. Here below with comments:

// From https://skalt.github.io/projects/brzozowski_ts/
type HexChar = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f";

type IsHex<Str extends string> = 
  Str extends ""
    ? true
    : Str extends `${HexChar}${infer Rest}`
      ? IsHex<Rest>
      : never;

// From https://stackoverflow.com/a/73369825
type StringLength<Str extends string, Acc extends 0[] = []> = 
  Str extends `${string}${infer $Rest}`
    ? StringLength<$Rest, [...Acc, 0]>
    : Acc["length"];

type IsLength<S extends string, Length extends number> = StringLength<S> extends Length ? true : false

// A custom field id is 40 char hex string
type CustomFieldId<T extends string> = 
  IsHex<T> extends true
    ? IsLength<T, 40> extends true
      ? string
      : never
    : never

type WithCustomFields<T, BaseType> = {
  [Key in keyof T]:
    Key extends string
      ? Key extends keyof T
        ? Value[Key]
        : CustomFieldId<Key>
      : never
}

// --- Example implementation

export type AddOrganizationRequest = {
    'name'?: string;
    'owner_id'?: number;
    'add_time'?: string;
    'update_time'?: string;
}

// We let arg be broader than AddOrganizationRequest by defining it as extends and pass the basic blueprint as K. This allow to compare and keep types of BaseType untouched and iterate on T and allow extension if some keyof T are CustomFieldId

function test<T extends BaseType, BaseType = AddOrganizationRequest>(arg: WithCustomFields<T, BaseType>) {}

test({
  name: 'foo',
  owner_id: 0,
  add_time: 'foo',
  update_time: 'foo',
  ['deadbeefdeadbeefdeadbeefdeadbeefdeadbeef']: 'foo', // allowed
})

test({
  name: 'foo',
  owner_id: 0,
  add_time: 'foo',
  update_time: 'foo',
  ['qwe']: 'foo',  // denied
  ['deadbeefdeadbeefdeadbeefdeadbeefdeadbeef']: 'foo', // allowed
})

test({
  name: 'foo',
  owner_id: 0,
  add_time: 'foo',
  update_time: 'foo',
  ['qwe']: 'foo', // denied
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions