TypeScript type ignore case

I have a definition of this type in TypeScript:

export type xhrTypes = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | "CONNECT" | "HEAD";

      

Unfortunately this is case sensitive ... is there a way to make it case insensitive?

thank

+3


source to share


2 answers


There is simply an answer to this post: No, it is impossible.



Update 5/15/2018 : still not possible. The closest thing, regex validated strings, was not accepted as recently as it was proposed in a language design meeting.

+5


source


As @RyanCavanaugh said, TypeScript doesn't have case-insensitive string literals. [EDIT: I was reminded that there is a proposal for TypeScript to support regex string literals that might allow this, but it is not part of the language at the moment.]

The only workaround I can think of is to list the most likely variations on these literals (say, all lowercase, initialization cap) and create a function that can translate between them as needed:

namespace XhrTypes {
  function m<T, K extends string, V extends string>(
    t: T, ks: K[], v: V
  ): T & Record<K | V, V> {
    (t as any)[v] = v;
    ks.forEach(k => (t as any)[k] = v);
    return t as any;
  }
  function id<T>(t: T): { [K in keyof T]: T[K] } {
    return t;
  }
  const mapping = id(m(m(m(m(m(m(m({},
    ["get", "Get"], "GET"), ["post", "Post"], "POST"),
    ["put", "Put"], "PUT"), ["delete", "Delete"], "DELETE"),
    ["options", "Options"], "OPTIONS"), ["connect", "Connect"], "CONNECT"),
    ["head", "Head"], "HEAD"));      

  export type Insensitive = keyof typeof mapping
  type ForwardMapping<I extends Insensitive> = typeof mapping[I];

  export type Sensitive = ForwardMapping<Insensitive>;     
  type ReverseMapping<S extends Sensitive> = 
    {[K in Insensitive]: ForwardMapping<K> extends S ? K : never}[Insensitive];

  export function toSensitive<K extends Insensitive>(
    k: K ): ForwardMapping<K> {
    return mapping[k];
  }

  export function matches<K extends Insensitive, L extends Insensitive>(
    k: K, l: L ): k is K & ReverseMapping<ForwardMapping<L>> {
    return toSensitive(k) === toSensitive(l);
  }
}

      

As a result, you get the following types:

type XhrTypes.Sensitive = "GET" | "POST" | "PUT" | "DELETE" | 
  "OPTIONS" | "CONNECT" | "HEAD"

type XhrTypes.Insensitive = "get" | "Get" | "GET" | 
  "post" | "Post" | "POST" | "put" | "Put" | "PUT" | 
  "delete" | "Delete" | "DELETE" | "options" | "Options" |
  "OPTIONS" | "connect" | "Connect" | "CONNECT" | "head" | 
  "Head" | "HEAD"

      

and functions



 function XhrTypes.toSensitive(k: XhrTypes.Insensitive): XhrTypes.Sensitive;

 function XhrTypes.matches(k: XhrTypes.Insensitive, l: XhrTypes.Insensitive): boolean;

      

I'm not sure if you (@Knu) need this for or how you plan to use it, but I imagine you want to convert between sensitive / insensitive methods, or check if two case-insensitive methods are a match. Obviously you can do this at runtime by simply uppercasing it or by doing a case insensitive comparison, but at compile time, the above types can be useful.

Here's an example of using it:

interface HttpStuff {
  url: string,
  method: XhrTypes.Insensitive,
  body?: any
}
const httpStuff: HttpStuff = {
  url: "https://google.com",
  method: "get"
}

interface StrictHttpStuff {
  url: string,
  method: XhrTypes.Sensitive,
  body?: any
}
declare function needStrictHttpStuff(httpStuff: StrictHttpStuff): Promise<{}>;

needStrictHttpStuff(httpStuff); // error, bad method

needStrictHttpStuff({
   url: httpStuff.url, 
   method: XhrTypes.toSensitive(httpStuff.method) 
  }); // okay

      

The function above has a function that expects an uppercase value, but you can safely pass it a case insensitive value if you use it first XhrTypes.toSensitive()

and the compiler checks which "get"

is an acceptable option "get"

in this case.

Ok, hope this helps. Good luck.

+1


source







All Articles