Typescript 类型脚本类型忽略大小写

Typescript 类型脚本类型忽略大小写,typescript,types,case-insensitive,typescript-typings,Typescript,Types,Case Insensitive,Typescript Typings,我在TypeScript中有此类型定义: export type xhrTypes = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | "CONNECT" | "HEAD"; 不幸的是,这是区分大小写的…有没有办法定义它不区分大小写 谢谢,这篇文章有一个答案:不,这是不可能的 2018年5月15日更新:仍然不可能。最近一次在语言设计会议上提出的最接近regex验证的字符串类型没有得到很好的接受。正如@RyanCavanaugh所说,TypeScr

我在TypeScript中有此类型定义:

export type xhrTypes = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | "CONNECT" | "HEAD";
不幸的是,这是区分大小写的…有没有办法定义它不区分大小写


谢谢,这篇文章有一个答案:不,这是不可能的


2018年5月15日更新:仍然不可能。最近一次在语言设计会议上提出的最接近regex验证的字符串类型没有得到很好的接受。

正如@RyanCavanaugh所说,TypeScript没有不区分大小写的字符串文本。[编辑:有人提醒我,现有的for-TypeScript支持regexp验证的字符串文字,这可能允许这样做,但它目前不是该语言的一部分。]

我能想到的唯一解决办法是列举这些文字最可能的变体(例如,所有小写字母,init cap),并生成一个可以在需要时在它们之间进行转换的函数:

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);
  }
}
以及功能

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

 function XhrTypes.matches(k: XhrTypes.Insensitive, l: XhrTypes.Insensitive): boolean;
我不确定你(@Knu)需要它做什么,或者你打算如何使用它,但我想你想在敏感/不敏感方法之间转换,或者检查两个不区分大小写的方法是否匹配。显然,您可以在运行时通过转换为大写或进行不区分大小写的比较来完成这些操作,但在编译时,上述类型可能会很有用

下面是一个使用它的示例:

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
接口HttpStuff{
url:string,
方法:XhrTypes.不敏感,
身体?:有吗
}
常量httpStuff:httpStuff={
url:“https://google.com",
方法:“获取”
}
HttpStuff接口{
url:string,
方法:XhrTypes.Sensitive,
身体?:有吗
}
声明函数needStrictHttpStuff(httpStuff:StrictHttpStuff):承诺;
需要严格的httpStuff(httpStuff);//错误,坏方法
所需材料({
url:httpStuff.url,
方法:XhrTypes.toSensitive(httpStuff.method)
}); // 可以
在上面的例子中,有一个函数需要大写值,但是如果您首先使用
XhrTypes.toSensitive()
,并且编译器验证在这种情况下
“get”
“get”
的可接受变体,则可以安全地传递一个不区分大小写的值


好的,希望能有帮助。祝你好运。

虽然不是要求的类型,但如果枚举可以,则可以使用以下内容进行枚举字符串值的不区分大小写匹配:

/**
*获取给定不区分大小写键的枚举。对于此使用的数字枚举
*其成员的姓名;对于字符串枚举,它将搜索特定的字符串值。
*如果忽略字母大小写以查找匹配项,则记录警告,并记录错误
*如果未找到匹配项,则包括支持的值。
*/
静态toEnumIgnoreCase(target:T,caseInsentiveKey:string):T[keyof T]{
常量指针=caseInsentiveKey.toLowerCase();
//如果枚举对象没有键“0”,则假定为字符串枚举
常量键=对象键(目标)
.find(k=>(目标['0']?k:target[k]).toLowerCase()==指针);
如果(!键){
const expected=Object.keys(目标)
.map(k=>target['0']?k:target[k])
.filter(k=>isNaN(Number.parseInt(k)))
。加入(‘,’);
错误(`无法将'${caseInsentiveKey}'映射到值${expected}`);
返回未定义;
}
const name=target['0']?键:target[key];
如果(名称!==caseInsentiveKey){
warn(`Ignored case to map${caseInsentiveKey}到value${name}`);
}
返回目标[键];
}
当然,当这个循环遍历可能的值时,它实际上只用于处理配置文件之类的事情;所有代码实际上都应该使用
enum

一些测试:

import Spy=jasmine.Spy;
从“/config helper”导入{ConfigHelper};
//应匹配一个、一个、一个和所有:
枚举数numbernum{1,2,3}
//应匹配Uno、Uno、Uno和所有,但不匹配一、一、一和所有:
枚举字符串枚举{1='Uno',2='Dos',3='Tres'}
描述('toEnumIgnoreCase',()=>{
beforeach(函数(){
间谍(控制台,“警告”);
spyOn(控制台,“错误”);
});
它('应该找到数字枚举的精确匹配',()=>{
const result=ConfigHelper.toEnumIgnoreCase(NumberEnum,'One');
期望(结果)toBe(第一);
expect(console.warn).not.tohaveEncalled();
expect(console.error).not.tohaveEncalled();
});
它('应该找到数字枚举的不区分大小写的匹配',()=>{
const result=ConfigHelper.toEnumIgnoreCase(NumberEnum,'two');
期望(结果)toBe(数字2);
expect(console.warn).tohavebeencall();
expect((console.warn as Spy).calls.mostRecent().args[0])
.toMatch(/值二/);
expect(console.error).not.tohaveEncalled();
});
它('对于数值枚举的不匹配,应产生未定义的',()=>{
const result=ConfigHelper.toEnumIgnoreCase(NumberEnum,'none');
expect(结果).toBe(未定义);
expect(console.warn).not.tohaveEncalled();
expect(console.error).toHaveBeenCalled();
expect((console.error as Spy).calls.mostRecent().args[0])
.toMatch(/值一、二、三/);
});
它('应该找到字符串枚举的精确匹配',()=>{
const result=ConfigHelper.toEnumIgnoreCase(StringEnum,'Uno');
expect(result).toBe(StringEnum.One);
expect(console.warn).not.tohaveEncalled();
expect(console.error).not.tohaveEncalled();
});
它('应该找到字符串枚举的不区分大小写的匹配',()=>{
const result=ConfigHelper.toEnumIgnoreCase(StringEnum,'dos');
expect(result).toBe(StringEnum.Two);
expect(console.warn).tohavebeencall();
expect((console.warn as Spy).calls.mostRecent().args[0])
.toMatch(/value-Dos/);
expect(console.error).not.tohaveEncalled();
});
它('应该为名称而不是字符串值生成未定义的',()=>{
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
type xhrTypes = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | "CONNECT" | "HEAD";

type AnyCase<T extends string> =
    string extends T ? string :
    T extends `${infer F1}${infer F2}${infer R}` ? (
        `${Uppercase<F1> | Lowercase<F1>}${Uppercase<F2> | Lowercase<F2>}${AnyCase<R>}`
    ) :
    T extends `${infer F}${infer R}` ? `${Uppercase<F> | Lowercase<F>}${AnyCase<R>}` :
    ""


type AnyCaseXhrTypes = AnyCase<xhrTypes>;
/* type AnyCaseXhrTypes = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | 
"CONNECT" | "HEAD" | "GEt" | "GeT" | "Get" | "gET" | "gEt" | "geT" | "get" | 
"POSt" | "POsT" | "POst" | "PoST" |  "PoSt" | "PosT" | "Post" | 
... 346 more ... | "head" */
function acceptAnyCaseXhrType(xhrType: AnyCaseXhrTypes) { }

acceptAnyCaseXhrType("get"); // okay
acceptAnyCaseXhrType("DeLeTe"); // okay
acceptAnyCaseXhrType("poot"); // error! "poot" not assignable to big union
type xhrTypes = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | "CONNECT" | "HEAD"
 | "LONG STRINGS MAKE THE COMPILER UNHAPPY";

type AnyCaseXhrTypes = AnyCase<xhrTypes>; // error!
// Type instantiation is excessively deep and possibly infinite.
// Union type is too complex to represent
function acceptAnyCaseXhrTypeGeneric<T extends string>(
    xhrType: Uppercase<T> extends xhrTypes ? T : xhrTypes
) { }

acceptAnyCaseXhrTypeGeneric("get"); // okay
acceptAnyCaseXhrTypeGeneric("DeLeTe"); // okay
acceptAnyCaseXhrTypeGeneric("poot"); // error! "poot" not assignable to xhrTypes