Front-End/TypeScript

[Type-challenges] 108, 110, 116, 119, 191

Y0ungZ 2024. 3. 15. 14:12
🍀 목차
108 - Trim
110 - Capitalize
116 - Replace
119 - ReplaceAll
191 - Append Argument

 

108 - Trim

// 정확한 문자열 타입이고 양쪽 끝의 공백이 제거된 새 문자열을 반환하는 Trim<T>를 구현하십시오.

// 예시
type trimmed = Trim<'  Hello World  '> // 기대되는 결과는 'Hello World'입니다.
/* _____________ 여기에 코드 입력 _____________ */

type Trim<S extends string> = any

/* _____________ 테스트 케이스 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Trim<'str'>, 'str'>>,
  Expect<Equal<Trim<' str'>, 'str'>>,
  Expect<Equal<Trim<'     str'>, 'str'>>,
  Expect<Equal<Trim<'str   '>, 'str'>>,
  Expect<Equal<Trim<'     str     '>, 'str'>>,
  Expect<Equal<Trim<'   \n\t foo bar \t'>, 'foo bar'>>,
  Expect<Equal<Trim<''>, ''>>,
  Expect<Equal<Trim<' \n\t '>, ''>>,
]
type Space = ' ' | '\t' | '\n';
type Trim<S extends string> = S extends `${Space}${infer T}` | `${infer T}${Space}` ? Trim<T> : S;


110 - Capitalize

// 문자열의 첫 글자만 대문자로 바꾸고 나머지는 그대로 놔두는 Capitalize<T>를 구현하세요.

// 예시
type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'

 

/* _____________ 여기에 코드 입력 _____________ */

type MyCapitalize<S extends string> = any

/* _____________ 테스트 케이스 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<MyCapitalize<'foobar'>, 'Foobar'>>,
  Expect<Equal<MyCapitalize<'FOOBAR'>, 'FOOBAR'>>,
  Expect<Equal<MyCapitalize<'foo bar'>, 'Foo bar'>>,
  Expect<Equal<MyCapitalize<''>, ''>>,
  Expect<Equal<MyCapitalize<'a'>, 'A'>>,
  Expect<Equal<MyCapitalize<'b'>, 'B'>>,
  Expect<Equal<MyCapitalize<'c'>, 'C'>>,
  Expect<Equal<MyCapitalize<'d'>, 'D'>>,
  Expect<Equal<MyCapitalize<'e'>, 'E'>>,
  Expect<Equal<MyCapitalize<'f'>, 'F'>>,
  Expect<Equal<MyCapitalize<'g'>, 'G'>>,
  Expect<Equal<MyCapitalize<'h'>, 'H'>>,
  Expect<Equal<MyCapitalize<'i'>, 'I'>>,
  Expect<Equal<MyCapitalize<'j'>, 'J'>>,
  Expect<Equal<MyCapitalize<'k'>, 'K'>>,
  Expect<Equal<MyCapitalize<'l'>, 'L'>>,
  Expect<Equal<MyCapitalize<'m'>, 'M'>>,
  Expect<Equal<MyCapitalize<'n'>, 'N'>>,
  Expect<Equal<MyCapitalize<'o'>, 'O'>>,
  Expect<Equal<MyCapitalize<'p'>, 'P'>>,
  Expect<Equal<MyCapitalize<'q'>, 'Q'>>,
  Expect<Equal<MyCapitalize<'r'>, 'R'>>,
  Expect<Equal<MyCapitalize<'s'>, 'S'>>,
  Expect<Equal<MyCapitalize<'t'>, 'T'>>,
  Expect<Equal<MyCapitalize<'u'>, 'U'>>,
  Expect<Equal<MyCapitalize<'v'>, 'V'>>,
  Expect<Equal<MyCapitalize<'w'>, 'W'>>,
  Expect<Equal<MyCapitalize<'x'>, 'X'>>,
  Expect<Equal<MyCapitalize<'y'>, 'Y'>>,
  Expect<Equal<MyCapitalize<'z'>, 'Z'>>,
]

 

type MyCapitalize<S extends string> = S extends `${infer head}${infer tail}` ? `${Uppercase<head>}${tail}` : S;

 

Template Literal Types로 Uppercase<StrintType>을 제공한다. 빌트인 타입을 사용하지 않으려면 객체로 대문자(key)를 소문자(value)로 매치시킬 수도 있다.

 

 

116 - Replace

// 문자열 S에서 From를 찾아 한 번만 To로 교체하는 Replace<S, From, To>를 구현하세요.

// 예시

type replaced = Replace<'types are fun!', 'fun', 'awesome'> 
// expected to be 'types are awesome!'
/* _____________ 여기에 코드 입력 _____________ */

type Replace<S extends string, From extends string, To extends string> = any

/* _____________ 테스트 케이스 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Replace<'foobar', 'bar', 'foo'>, 'foofoo'>>,
  Expect<Equal<Replace<'foobarbar', 'bar', 'foo'>, 'foofoobar'>>,
  Expect<Equal<Replace<'foobarbar', '', 'foo'>, 'foobarbar'>>,
  Expect<Equal<Replace<'foobarbar', 'bar', ''>, 'foobar'>>,
  Expect<Equal<Replace<'foobarbar', 'bra', 'foo'>, 'foobarbar'>>,
  Expect<Equal<Replace<'', '', ''>, ''>>,
]

 

type Replace<S extends string, From extends string, To extends string> = S extends `${infer Left}${From}${infer Right}` ? `${Left}${To}${Right}` : S;

 

 From이 공백일 때를 처리해주지 않으면 Replace<'foobarbar', '', 'foo'>의 결과가 'foobarbar'가 아닌 'ffoooobarbar'가 된다. 

From이 공백일 때는 never이 반환되게 해준다.

 

type Replace<S extends string, From extends string, To extends string> = S extends `${infer Left}${From extends '' ? never: From}${infer Right}` ? `${Left}${To}${Right}` : S;

 

 

119 - ReplaceAll

// 주어진 문자열 S에서 부분 문자열 From을 찾아 모두 To로 교체하는 \
// 제네릭 ReplaceAll<S, From, To>을 구현하세요.

// 예시
type replaced = ReplaceAll<'t y p e s', ' ', ''> // expected to be 'types'
/* _____________ 여기에 코드 입력 _____________ */

type ReplaceAll<S extends string, From extends string, To extends string> = any

/* _____________ 테스트 케이스 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<ReplaceAll<'foobar', 'bar', 'foo'>, 'foofoo'>>,
  Expect<Equal<ReplaceAll<'foobar', 'bag', 'foo'>, 'foobar'>>,
  Expect<Equal<ReplaceAll<'foobarbar', 'bar', 'foo'>, 'foofoofoo'>>,
  Expect<Equal<ReplaceAll<'t y p e s', ' ', ''>, 'types'>>,
  Expect<Equal<ReplaceAll<'foobarbar', '', 'foo'>, 'foobarbar'>>,
  Expect<Equal<ReplaceAll<'barfoo', 'bar', 'foo'>, 'foofoo'>>,
  Expect<Equal<ReplaceAll<'foobarfoobar', 'ob', 'b'>, 'fobarfobar'>>,
  Expect<Equal<ReplaceAll<'foboorfoboar', 'bo', 'b'>, 'foborfobar'>>,
  Expect<Equal<ReplaceAll<'', '', ''>, ''>>,
]

 

type ReplaceAll<S extends string, From extends string, To extends string> = 
From extends '' ? 
S : S extends `${infer Left}${From}${infer Right}` ? 
`${ReplaceAll<Left, From, To>}${To}${ReplaceAll<Right,From,To>}` : S;


191 - Append Argument

// 함수 타입 Fn과 어떤 타입 A가 주어질 때 
// Fn의 인수와 A를 마지막 인수로 받는 Fn과 동일한 함수 유형인 G를 생성하세요.

// 예시
type Fn = (a: number, b: string) => number

type Result = AppendArgument<Fn, boolean> 
// 기대되는 결과는 (a: number, b: string, x: boolean) => number 입니다.
/* _____________ 여기에 코드 입력 _____________ */

type AppendArgument<Fn, A> = any

/* _____________ 테스트 케이스 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type Case1 = AppendArgument<(a: number, b: string) => number, boolean>
type Result1 = (a: number, b: string, x: boolean) => number

type Case2 = AppendArgument<() => void, undefined>
type Result2 = (x: undefined) => void

type cases = [
  Expect<Equal<Case1, Result1>>,
  Expect<Equal<Case2, Result2>>,
  // @ts-expect-error
  AppendArgument<unknown, undefined>,
]

 

 먼저, 삼항 연산자를 사용해 Fn이 함수 타입이 아니라면 never을 반환해 보자.

type AppendArgument<Fn extends Function, A> = Fn extends (...args: infer Args) => infer T? T : never;

 

반환해야 할 것은 함수의 반환값 타입만이 아닌 Fn의 매개변수에 A를 더한 새로운 함수므로 T를 아래와 같이 수정한다.

(...args : [...Args, A]) => T

 

기존 argument에 A를 추가하고, Fn의 반환값인 T를 그대로 반환한다.

최종적으로 정답은 아래와 같다.

type AppendArgument<Fn extends Function, A> = Fn extends (...args : infer Args) => infer T ? (...args : [...Args, A]) => T : never;

 

 

 

참고자료

https://github.com/type-challenges/type-challenges/blob/main/questions/00108-medium-trim/README.ko.md

https://github.com/type-challenges/type-challenges/blob/main/questions/00110-medium-capitalize/README.ko.md

https://github.com/type-challenges/type-challenges/blob/main/questions/00116-medium-replace/README.ko.md

https://github.com/type-challenges/type-challenges/blob/main/questions/00119-medium-replaceall/README.ko.md

https://github.com/type-challenges/type-challenges/blob/main/questions/00191-medium-append-argument/README.ko.md