I have this type definition in TypeScript:
export type xhrTypes = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"OPTIONS\" | \"CONNECT\" | \"HEAD\";
As @RyanCavanaugh said, TypeScript doesn't have case-insensitive string literals. [EDIT: I am reminded that there is an existing suggestion for TypeScript to support regexp-validated string literals, which would maybe allow for this, but it is not currently part of the language.]
The only workaround I can think of is to enumerate the most likely variants of those literals (say, all lowercase, init cap) and make a function that can translate between them if needed:
namespace XhrTypes {
function m(
t: T, ks: K[], v: V
): T & Record {
(t as any)[v] = v;
ks.forEach(k => (t as any)[k] = v);
return t as any;
}
function id(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 = typeof mapping[I];
export type Sensitive = ForwardMapping;
type ReverseMapping =
{[K in Insensitive]: ForwardMapping extends S ? K : never}[Insensitive];
export function toSensitive(
k: K ): ForwardMapping {
return mapping[k];
}
export function matches(
k: K, l: L ): k is K & ReverseMapping> {
return toSensitive(k) === toSensitive(l);
}
}
What ends up getting exported is 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 the functions
function XhrTypes.toSensitive(k: XhrTypes.Insensitive): XhrTypes.Sensitive;
function XhrTypes.matches(k: XhrTypes.Insensitive, l: XhrTypes.Insensitive): boolean;
I'm not sure what you (@Knu) need this for or how you plan to use it, but I'm imagining that you want to convert between sensitive/insensitive methods, or check to see if two case-insensitive methods are a match. Obviously you can do those at runtime by just converting to uppercase or doing a case-insensitive compare, but at compile time the above types may 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
In the above, there's a function that expects uppercase values, but you can safely pass it a case insensitive value if you use XhrTypes.toSensitive()
first, and the compiler verifies that "get"
is an acceptable variant of "GET"
in this case.
Okay, hope that helps. Good luck.