TypeScript 유형 무시 대소문자
TypeScript에는 다음과 같은 형식 정의가 있습니다.
export type xhrTypes = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | "CONNECT" | "HEAD";
슬프게도, 이것은 대소문자를 구분합니다. 대소문자를 구분하지 않는 것으로 정의할 수 있는 방법이 있습니까?
감사해요.
유형 스크립트 4.1+에 대한 새로운 답변
다시 오신 것을 환영합니다!이제 TypeScript 4.1에 템플릿 리터럴 유형과 /Lowercase
intental 문자열 매핑 유형이 도입되었으므로 정규식 유형 없이도 이 질문에 대답할 수 있습니다.
두 가지 주요 접근 방식이 있습니다."잔인한 힘" 접근법은 반복적인 조건부 유형과 조합을 많이 사용하여 당신을 변화시킵니다.xhrTypes
사례가 중요하지 않은 문자열을 작성하는 모든 가능한 방법의 구체적인 결합:
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>;
를 AnyCaseXhrTypes
368명의 조합원으로 구성된 조합임을 알게 될 것입니다.
/* 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" */
여러분은 그다음다이사수있다습니용할유을 에 이 할 수 .xhrType
사례 불감증이 필요한 곳:
function acceptAnyCaseXhrType(xhrType: AnyCaseXhrTypes) { }
acceptAnyCaseXhrType("get"); // okay
acceptAnyCaseXhrType("DeLeTe"); // okay
acceptAnyCaseXhrType("poot"); // error! "poot" not assignable to big union
브루트 포스 접근 방식의 문제는 더 많거나 더 긴 문자열로 잘 확장되지 않는다는 것입니다.TypeScript의 Union 유형은 100,000개의 멤버로 제한되며, 재귀 조건부 유형은 컴파일러가 불평하기 전에 최대 20개 수준까지만 진행됩니다.따라서 적당히 긴 단어나 적당히 긴 단어 목록은 위의 접근법을 실행 불가능하게 만들 것입니다.
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
이 문제를 해결하는 방법은 특정한 구체적인 조합을 사용하는 것에서 벗어나 일반적인 유형의 표현으로 전환하는 것입니다.한다면T
는 전된문 유값형니다입의열로 입니다.acceptAnyCaseXhrType()
그렇다면 우리가 원하는 것은 확실하게 하는 것입니다.Uppercase<T>
할 수 .xhrType
이는 유형이라기보다는 제약 조건에 가깝습니다(일반 제약 조건을 직접 사용하여 표현할 수는 없지만).
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
이 솔루션을 사용하려면 일반 유형 매개 변수가 필요하지 않을 수 있는 위치에서 매개 변수를 끌어와야 하지만 확장성이 좋습니다.
자, 여기 있습니다!우리가 해야 할 일은...(비고음)...3년, TypeScript 제공!
단지 이 게시물에 답이 있습니다: 아니요, 그것은 불가능합니다.
2018년 5월 15일 업데이트: 여전히 불가능합니다.가장 가까운 것인 정규식 검증 문자열 유형은 언어 설계 회의에서 가장 최근에 제안되었을 때 좋은 평가를 받지 못했습니다.
@RyanCavanaugh가 말했듯이, TypeScript에는 대소문자를 구분하지 않는 문자열 리터럴이 없습니다.[편집: TypeScript가 정규화 검증된 문자열 리터럴을 지원하는 기존 제안이 있음을 상기합니다. 이를 허용할 수도 있지만 현재 이 언어의 일부는 아닙니다.]
제가 생각할 수 있는 유일한 해결책은 이러한 리터럴의 가장 가능성이 높은 변형(예: 모든 소문자, 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);
}
}
내보낼 수 있는 유형은 다음과 같습니다.
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"
그리고 기능들.
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
위에는 대문자 값을 예상하는 함수가 있지만 사용하면 대소문자를 구분하지 않는 값을 안전하게 전달할 수 있습니다.XhrTypes.toSensitive()
먼저, 그리고 컴파일러는 그것을 확인합니다."get"
의 허용 가능한 변형입니다."GET"
이 경우에는
좋아요, 도움이 되길 바랍니다.행운을 빌어요.
요청된 유형은 아니지만 열거형이 괜찮다면 다음을 대소문자를 구분하지 않는 열거형 문자열 값 일치에 사용할 수 있습니다.
/**
* Gets an enumeration given a case-insensitive key. For a numeric enum this uses
* its members' names; for a string enum this searches the specific string values.
* Logs a warning if the letter case was ignored to find a match, and logs an error
* including the supported values if no match was found.
*/
static toEnumIgnoreCase<T>(target: T, caseInsentiveKey: string): T[keyof T] {
const needle = caseInsentiveKey.toLowerCase();
// If the enum Object does not have a key "0", then assume a string enum
const key = Object.keys(target)
.find(k => (target['0'] ? k : target[k]).toLowerCase() === needle);
if (!key) {
const expected = Object.keys(target)
.map(k => target['0'] ? k : target[k])
.filter(k => isNaN(Number.parseInt(k)))
.join(', ');
console.error(`Could not map '${caseInsentiveKey}' to values ${expected}`);
return undefined;
}
const name = target['0'] ? key : target[key];
if (name !== caseInsentiveKey) {
console.warn(`Ignored case to map ${caseInsentiveKey} to value ${name}`);
}
return target[key];
}
물론, 이것은 가능한 값을 루프하기 때문에 구성 파일과 같은 것들만 처리하기 위한 것입니다. 모든 코드는 정말로 사용해야 합니다.enum
대신 값을 입력합니다.
일부 테스트:
import Spy = jasmine.Spy;
import {ConfigHelper} from './config-helper';
// Should match on One, one, ONE and all:
enum NumberEnum { One, Two, Three }
// Should match on Uno, uno, UNO and all, but NOT on One, one, ONE and all:
enum StringEnum { One = 'Uno', Two = 'Dos', Three = 'Tres' }
describe('toEnumIgnoreCase', () => {
beforeEach(function () {
spyOn(console, 'warn');
spyOn(console, 'error');
});
it('should find exact match for numeric enum', () => {
const result = ConfigHelper.toEnumIgnoreCase(NumberEnum, 'One');
expect(result).toBe(NumberEnum.One);
expect(console.warn).not.toHaveBeenCalled();
expect(console.error).not.toHaveBeenCalled();
});
it('should find case-insensitive match for numeric enum', () => {
const result = ConfigHelper.toEnumIgnoreCase(NumberEnum, 'two');
expect(result).toBe(NumberEnum.Two);
expect(console.warn).toHaveBeenCalled();
expect((console.warn as Spy).calls.mostRecent().args[0])
.toMatch(/value Two/);
expect(console.error).not.toHaveBeenCalled();
});
it('should yield undefined for non-match for numeric enum', () => {
const result = ConfigHelper.toEnumIgnoreCase(NumberEnum, 'none');
expect(result).toBe(undefined);
expect(console.warn).not.toHaveBeenCalled();
expect(console.error).toHaveBeenCalled();
expect((console.error as Spy).calls.mostRecent().args[0])
.toMatch(/values One, Two, Three/);
});
it('should find exact match for string enum', () => {
const result = ConfigHelper.toEnumIgnoreCase(StringEnum, 'Uno');
expect(result).toBe(StringEnum.One);
expect(console.warn).not.toHaveBeenCalled();
expect(console.error).not.toHaveBeenCalled();
});
it('should find case-insensitive match for string enum', () => {
const result = ConfigHelper.toEnumIgnoreCase(StringEnum, 'dos');
expect(result).toBe(StringEnum.Two);
expect(console.warn).toHaveBeenCalled();
expect((console.warn as Spy).calls.mostRecent().args[0])
.toMatch(/value Dos/);
expect(console.error).not.toHaveBeenCalled();
});
it('should yield undefined for name rather than string value', () => {
const result = ConfigHelper.toEnumIgnoreCase(StringEnum, 'One');
expect(result).toBe(undefined);
expect(console.warn).not.toHaveBeenCalled();
expect(console.error).toHaveBeenCalled();
expect((console.error as Spy).calls.mostRecent().args[0])
.toMatch(/values Uno, Dos, Tres/);
});
it('should yield undefined for non-match for string enum', () => {
const result = ConfigHelper.toEnumIgnoreCase(StringEnum, 'none');
expect(result).toBe(undefined);
expect(console.warn).not.toHaveBeenCalled();
expect(console.error).toHaveBeenCalled();
expect((console.error as Spy).calls.mostRecent().args[0])
.toMatch(/values Uno, Dos, Tres/);
});
});
언급URL : https://stackoverflow.com/questions/43677527/typescript-type-ignore-case
'sourcecode' 카테고리의 다른 글
SQL에서 해당 달의 마지막 날 가져오기 (0) | 2023.07.12 |
---|---|
GCM(현재 FCM)은 제한 없이 무료입니까? (0) | 2023.07.12 |
"메모리가 8바이트 정렬"이라는 것은 무엇을 의미합니까? (0) | 2023.07.07 |
mongoDB에서 업데이트를 사용한 집계 (0) | 2023.07.07 |
부트스트랩 양식에 두 필드를 나란히 표시 (0) | 2023.07.07 |