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
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.
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.