question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: return an `isValid` flag in the response to simplify calling code

See original GitHub issue

Firstly I’d like to say a huge thanks for this library. We’ve been using it for years in production to parse user-supplied mobile phone numbers into E.164 format, and it has worked beautifully well. ❤️

I noticed that the API response has changed for v3, which is great - it’s much nicer to work with an object as the response, rather than a tuple. That being said, I was hoping to propose a further improvement, based on an assumption that I’m making (which of course could be wrong).

Right now the response object takes the following format:

{
    phoneNumber: string | null;
    countryIso2: string | null;
    countryIso3: string | null;
    countryCode: string | null;
}

If I want to ensure that I always return a valid phone number and country ISO code, I have to do the following:

import phone from 'phone';

interface ParsedPhoneNumber {
  phoneNumber: string;
  countryIso2: string;
  countryIso3: string;
}

function parsePhoneNumber(phoneNumberInput: string): ParsedPhoneNumber | null {
  const { phoneNumber, countryIso2, countryIso3 } = phone(phoneNumberInput);

  // Explicitly check every field for null, so that TypeScript can infer that each is of type `string` below
  if(phoneNumber === null || countryIso2 === null || countryIso3 === null) {
    return null;
  }

  // At this point, TS has inferred that all three fields are of type `string` - i.e. the `null` types have been erased.
  // We can therefore return them all, satisfying the `ParsedPhoneNumber` return type.
  return {
    phoneNumber,
    countryIso2,
    countryIso3,
  };
}

However, if the following assumption is true, this can be simplified via the use of a discriminator field. The assumption is that either:

  • All of these properties will be strings, or
  • All of these properties will be null

Is that correct? If so, we can use TypeScript’s discriminated union support to simplify the above. Consider the following types:

interface PhoneNumberInvalidResponse {
  isValid: false;
}

interface PhoneNumberValidResponse {
  isValid: true;
  phoneNumber: string;
  countryIso2: string;
  countryIso3: string;
}

type PhoneNumberResponse = PhoneNumberInvalidResponse | PhoneNumberValidResponse; 

Now, if the phone library could return the type PhoneNumberResponse, i.e. either the valid or the invalid response, we could do the following in code:

import phone from 'phone';

interface ParsedPhoneNumber {
  phoneNumber: string;
  countryIso2: string;
  countryIso3: string;
}

function parsePhoneNumber(phoneNumberInput: string): ParsedPhoneNumber | null {
  const parsed = phone(phoneNumberInput);

  // We only need to check the `isValid` field, since this is a discriminator.
  if(!parsed.isValid) {
    return null;
  }

  // At this point, TS knows that `parsed.isValid` must be `true`.
  // It can therefore infer that the type of `parsed` must be `PhoneNumberValidResponse`,
  // and so pulling out these fields will guarantee they are of type `string`.
  // This means we don't need to explicitly check that each field is not `null`.
  const { phoneNumber, countryIso2, countryIso3 } = parsed;
  return {
    phoneNumber,
    countryIso2,
    countryIso3,
  };
}

Of course, this all depends on the assumption that if a phone number is valid, it also has the associated country codes returned with it. If this is the case, then using a discriminated union could arguably improve the data model, since it is currently acceptible (according to the return type) for countryIso2 to be a string and countryIso3 to be null.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
Bossa573commented, Jul 13, 2021

I think it indeed raise some usability issue, I’ll discuss with the team soon to inspect and improve the interface

0reactions
fiznoolcommented, Jul 15, 2021

FWIW I think the original proposal is sound, just that it would need to be released as a v4 given that it is a breaking change. But I can definitely see the benefits of always returning a phoneNumber field whether the isValid flag is set or not.

Thanks again for implementing this so quickly!

Read more comments on GitHub >

github_iconTop Results From Across the Web

How do I return the response from an asynchronous call?
There are basically two ways how to solve this: Make the AJAX call synchronous (let's call it SJAX). Restructure your code to work...
Read more >
Part 15 - Contracting by Negotiation | Acquisition.GOV
Proposal modification is a change made to a proposal before the solicitation closing date and time, or made in response to an amendment, ......
Read more >
Request and Response Codes - Visa Developer
Country Name Country Code (2‑char.) Country Code (3‑char.) Country Code (numeric) Ph... Afghanistan AF AFG 4 93 Albania AL ALB 8 355 Algeria DZ DZA 12...
Read more >
RFC 3261: SIP: Session Initiation Protocol
1 Generating the Request A valid SIP request formulated by a UAC MUST, at a minimum, contain the following header fields: To, From,...
Read more >
Token Introspection Endpoint - OAuth 2.0 Simplified
The token introspection endpoint needs to be able to return ... an error response, and the endpoint returns simply an inactive flag.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found