PercentageParser을 구현하세요. /^(\+|\-)?(\d*)?(\%)?$/
정규식에 따라 T를 일치시키고 3개의 일치 요소를 얻습니다
구조는 [더하기 혹은 빼기
, 숫자
,단위
]와 같아야 합니다.
일치 요소가 없다면, 기본값은 빈 문자열입니다.
예시
type PString1 = "";
type PString2 = "+85%";
type PString3 = "-85%";
type PString4 = "85%";
type PString5 = "85";
type R1 = PercentageParser<PString1>; // expected ['', '', '']
type R2 = PercentageParser<PString2>; // expected ["+", "85", "%"]
type R3 = PercentageParser<PString3>; // expected ["-", "85", "%"]
type R4 = PercentageParser<PString4>; // expected ["", "85", "%"]
type R5 = PercentageParser<PString5>; // expected ["", "85", ""]
접근 방식
코드
type IsSign<C extends string> = C extends "+" | "-" ? true : false;
type IsPercent<C extends string> = C extends "%" ? true : false;
type PercentageParser<A extends string> =
A extends `${infer First}${infer Middle}${infer Last}`
? IsSign<First> extends true
? IsPercent<Last> extends true
? [First, Middle, Last]
: [First, `${Middle}${Last}`, ""]
: IsPercent<Last> extends true
? ["", `${First}${Middle}`, Last]
: ["", `${First}${Middle}${Last}`, ""]
: ["", "", ""];
실패 이유
접근 방식
코드
type IsSign<C extends string> = C extends "+" | "-" ? true : false;
type IsPercent<C extends string> = C extends "%" ? true : false;
type Last<T extends any[]> = T extends [...infer _, infer Last extends string]
? Last
: "";
type Join<T extends string[]> = T extends [
infer First extends string,
...infer Rest extends string[],
]
? `${First}${Join<Rest>}`
: "";
type RemoveLast<T extends string[]> = T extends [
...infer Rest extends string[],
infer _,
]
? Join<Rest>
: "";
type StringToTuple<S extends String> = S extends `${infer First}${infer Last}`
? [First, ...StringToTuple<Last>]
: [];
type PercentageParser<A extends string> =
StringToTuple<A> extends [
infer First extends string,
...infer Rest extends string[],
]
? IsSign<First> extends true
? IsPercent<Last<Rest>> extends true
? [First, RemoveLast<Rest>, Last<Rest>]
: [First, Join<Rest>, ""]
: IsPercent<First> extends true
? ["", "", "%"]
: IsPercent<Last<Rest>> extends true
? ["", RemoveLast<[First, ...Rest]>, Last<Rest>]
: ["", Join<[First, ...Rest]>, ""]
: ["", "", ""];
코드 설명
IsSign<C extends string>
: 한 글자(Character)를 받아 해당 요소가 부호(+, -)인지 확인IsPercent<C extends string>
: 한 글자(Character)를 받아 해당 요소가 단위(%)인지 확인Last<T extends any[]>
: 배열을 받아 마지막 요소를 반환Join<T extends string[]>
: 배열을 받아 문자열로 결합RemoveLast<T extends string[]>
: 배열을 받아 마지막 요소를 제거StringToTuple<S extends string>
: 문자열을 받아 튜플로 변환지정된 문자를 문자열에서 제거하는 DropChar<T, C>
를 구현하세요.
예시
type Butterfly = DropChar<" b u t t e r f l y ! ", " ">; // 'butterfly!'
접근 방식
코드
type DropChar<
S extends string,
C extends string,
> = S extends `${infer Head}${C}${infer Tail}`
? DropChar<`${Head}${Tail}`, C>
: S;
코드 설명
${infer Head}${C}${infer Tail}
를 활용해 S 안에 C가 있는지 확인주어진 숫자(항상 양수) 타입을 받아 1을 빼는 MinusOne<T>
를 구현하세요. 반환 타입은 숫자 타입이어야 합니다.
예시
type Zero = MinusOne<1>; // 0
type FiftyFour = MinusOne<55>; // 54
접근 방식
코드
// 한 자리 수의 마이너스 1 처리용 타입
type CalcMinusOne = {
[key: string]: string;
"1": "0";
"2": "1";
"3": "2";
"4": "3";
"5": "4";
"6": "5";
"7": "6";
"8": "7";
"9": "8";
"0": "9";
};
// 문자열 배열(튜플)의 마지막 요소 추출
type Last<T extends string[]> = T extends [...infer _, infer Last]
? Last
: never;
// 문자열을 튜플로 변환
type StringToTuple<S extends String> = S extends `${infer First}${infer Last}`
? [First, ...StringToTuple<Last>]
: [];
// 튜플을 문자열로 변환
type Join<T extends string[]> = T extends [
infer First extends string,
...infer Rest extends string[],
]
? `${First}${Join<Rest>}`
: "";
// 마지막 요소를 제거한 튜플을 문자열로 변환
type RemoveLast<T extends string[]> = T extends [
...infer Rest extends string[],
infer _,
]
? Join<Rest>
: "";
// 문자열을 숫자로 변환
type StringToNumber<S extends string> = S extends `${infer N extends number}`
? N
: never;
// 문자열 배열(튜플)의 마지막 요소가 0인 경우, 마지막 요소를 제거한 문자열 배열(튜플)을 문자열로 변환
// 만약 마지막 요소가 0이면 9로 변환, 그리고 앞 요소에 대해서도 재귀 돌리기
type MinusOneInStringArray<
S extends string,
ST extends string[] = StringToTuple<S>,
> =
Last<ST> extends "0"
? S extends "10"
? "9"
: `${MinusOneInStringArray<RemoveLast<ST>>}9`
: Last<ST> extends "_"
? `${MinusOneInStringArray<RemoveLast<ST>>}_`
: `${RemoveLast<ST>}${CalcMinusOne[Last<ST>]}`;
// 재귀 돌리기 전처리
type MinusOne<T extends number, S extends string[] = StringToTuple<`${T}`>> =
Last<S> extends "0"
? StringToNumber<`${MinusOneInStringArray<RemoveLast<S>>}9`>
: StringToNumber<`${RemoveLast<S>}${CalcMinusOne[Last<S>]}`>;
코드 설명
CalcMinusOne
타입: 한 자리 수의 마이너스 1 처리용 타입StringToTuple<S>
: 문자열을 튜플로 변환Join<T extends string[]>
: 튜플을 문자열로 변환RemoveLast<T extends string[]>
: 튜플의 마지막 요소를 제거하여 문자열로 조인해서 반환StringToNumber<S extends string>
: 문자열을 숫자로 변환Last<T extends string[]>
: 튜플의 마지막 요소 추출MinusOneInStringArray<S>
: 문자열 배열(튜플)의 마지막 요소가 0인 경우, 마지막 요소를 제거한 문자열 배열(튜플)을 문자열로 변환MinusOne<T>
: 재귀 돌리기, 최초 조건부 처리T
에서 U
타입을 할당할 수 있는 속성만 선택하는 PickByType<T, U>
를 구현하세요.
예시
type OnlyBoolean = PickByType<
{
name: string;
count: number;
isReadonly: boolean;
isEnable: boolean;
},
boolean
>; // { isReadonly: boolean; isEnable: boolean; }
접근 방식
코드
type PickByType<T, U> = { [K in keyof T as T[K] extends U ? K : never]: T[K] };
코드 설명
T[K] extends U ? K : never
조건의 키-리맵핑을 통해 T[K]의 타입이 U에 할당 가능한 경우, 해당 키만 남기고 나머지는 제거문자열 T
가 문자열 U
로 시작하는지 확인하는 StartsWith<T, U>
를 구현하세요.
예시
type a = StartsWith<"abc", "ac">; // expected to be false
type b = StartsWith<"abc", "ab">; // expected to be true
type c = StartsWith<"abc", "abcd">; // expected to be false
접근 방식
코드
type StartsWith<T extends string, U extends string> = T extends U
? true
: T extends `${infer TFirst}${infer TRest}`
? U extends `${infer UFirst}${infer URest}`
? TFirst extends UFirst
? StartsWith<TRest, URest>
: false
: true
: false;
코드 설명
T extends U
조건: 문자열 T가 U와 일치하는지 확인문자열 T
가 문자열 U
로 끝나는지 확인하는 EndsWith<T, U>
를 구현하세요.
예시
type a = EndsWith<"abc", "bc">; // expected to be true
type b = EndsWith<"abc", "abc">; // expected to be true
type c = EndsWith<"abc", "d">; // expected to be false
접근 방식
코드
type ReverseString<S extends string> = S extends `${infer First}${infer Rest}`
? `${ReverseString<Rest>}${First}`
: S;
type StartsWith<T extends string, U extends string> = T extends U
? true
: T extends `${infer TFirst}${infer TRest}`
? U extends `${infer UFirst}${infer URest}`
? TFirst extends UFirst
? StartsWith<TRest, URest>
: false
: true
: false;
type EndsWith<T extends string, U extends string> = StartsWith<
ReverseString<T>,
ReverseString<U>
>;
코드 설명
ReverseString<T>
: 문자열 T를 뒤집은 문자열StartsWith<ReverseString<T>, ReverseString<U>>
: 뒤집은 문자열 T와 U를 비교