Front-End/TypeScript

[Type-challenges] 531, 599, 612, 645, 949

Y0ungZ 2024. 4. 6. 06:20
🍀 목차
531 - String to Union
599 - Merge
612 - KebabCase
645 - Diff
949 - AnyOf

 

531 - String to Union

// 문자열 인수를 입력받는 String to Union 유형을 구현하세요. 
// 출력은 입력 문자열의 Union type이어야 합니다.

// 예시
type Test = "123"
type Result = StringToUnion<Test> // expected to be "1" | "2" | "3"
/* _____________ 여기에 코드 입력 _____________ */

type StringToUnion<T extends string> = any

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

type cases = [
  Expect<Equal<StringToUnion<''>, never>>,
  Expect<Equal<StringToUnion<'t'>, 't'>>,
  Expect<Equal<StringToUnion<'hello'>, 'h' | 'e' | 'l' | 'l' | 'o'>>,
  Expect<Equal<StringToUnion<'coronavirus'>, 'c' | 'o' | 'r' | 'o' | 'n' | 'a' | 'v' | 'i' | 'r' | 'u' | 's'>>,
]

 

type StringToUnion<T extends string> = T extends `${infer First}${infer Rest}` ? First | StringToUnion<Rest> : never;


599 - Merge

// 두개의 타입을 새로운 타입으로 병합하세요. 
// 두번째 타입의 Key가 첫번째 타입을 덮어씁니다(재정의합니다)

// 예시
type foo = {
  name: string
  age: string
}
type coo = {
  age: number
  sex: string
}

type Result = Merge<foo, coo> // expected to be {name: string, age: number, sex: string}
/* _____________ 여기에 코드 입력 _____________ */

type Merge<F, S> = any

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

type Foo = {
  a: number
  b: string
}
type Bar = {
  b: number
  c: boolean
}

type cases = [
  Expect<Equal<Merge<Foo, Bar>, {
    a: number
    b: number
    c: boolean
  }>>,
]

 

type Merge<F, S> = {
  [key in keyof F | keyof S]: key extends keyof S ? S[key] : key extends keyof F ? F[key] : never;
}


612 - KebabCase

// camelCase나 PascalCase를 kebab-case 문자열로 수정하세요.
// FooBarBaz -> foo-bar-baz

// 예시
type FooBarBaz = KebabCase<"FooBarBaz">
const foobarbaz: FooBarBaz = "foo-bar-baz"

type DoNothing = KebabCase<"do-nothing">
const doNothing: DoNothing = "do-nothing"
/* _____________ 여기에 코드 입력 _____________ */

type KebabCase<S> = any

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

type cases = [
  Expect<Equal<KebabCase<'FooBarBaz'>, 'foo-bar-baz'>>,
  Expect<Equal<KebabCase<'fooBarBaz'>, 'foo-bar-baz'>>,
  Expect<Equal<KebabCase<'foo-bar'>, 'foo-bar'>>,
  Expect<Equal<KebabCase<'foo_bar'>, 'foo_bar'>>,
  Expect<Equal<KebabCase<'Foo-Bar'>, 'foo--bar'>>,
  Expect<Equal<KebabCase<'ABC'>, 'a-b-c'>>,
  Expect<Equal<KebabCase<'-'>, '-'>>,
  Expect<Equal<KebabCase<''>, ''>>,
  Expect<Equal<KebabCase<'😎'>, '😎'>>,
]

 

type KebabCase<S extends string> = 
S extends `${infer First}${infer Rest}`
? Rest extends Uncapitalize<Rest>
? `${Uncapitalize<First>}${KebabCase<Rest>}`
: `${Uncapitalize<First>}-${KebabCase<Rest>}`
: S;

 

 Uncapitalize<StringType> (문서)는 전달받은 string의 첫 문자를 소문자로 변경한다. Rest extends Uncapitalize<Rest>로 뒤에 있는 문자가 소문자로 시작한다면 기존 string 첫 문자 뒤에 바로, 그렇지 않다면 하이픈(-)을 붙인 후, 다시 Rest에 대해서 KebabCase 로직을 실행한다.

 


645 - Diff

// O & O1의 차이점인 객체를 가져옵니다
/* _____________ 여기에 코드 입력 _____________ */

type Diff<O, O1> = any

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

type Foo = {
  name: string
  age: string
}
type Bar = {
  name: string
  age: string
  gender: number
}
type Coo = {
  name: string
  gender: number
}

type cases = [
  Expect<Equal<Diff<Foo, Bar>, { gender: number }>>,
  Expect<Equal<Diff<Bar, Foo>, { gender: number }>>,
  Expect<Equal<Diff<Foo, Coo>, { age: string, gender: number }>>,
  Expect<Equal<Diff<Coo, Foo>, { age: string, gender: number }>>,
]

 

type Foo = {
  name: string
  age: string
}
type Bar = {
  name: string
  age: string
  gender: number
}
type test1 = keyof (Foo&Bar);
// "name" | "age" | "gender"

type test2 = keyof (Foo|Bar);
// "name" | "age"

 

위의 결과들과 Exclude를 사용해서 keyof (Foo&Bar)에서 keyof (Foo|Bar)을 제외시킨다.

type MyExclude<T,U> = T extends U ? never : T;

type test = MyExclude<keyof (Foo&Bar), keyof (Foo|Bar)>
// "gender"

 

Omit을 사용하여 도출된 속성을 제거한 타입을 정의하면 된다.

type MyExclude<T,U> = T extends U ? never : T;
type MyOmit<T,K> = {
  [key in MyExclude<keyof T, keyof K>] : T[key]
}

type Diff<O, O1> = MyOmit<O&O1, O|O1>

 


949 - AnyOf

// Implement Python liked any function in the type system.
// A type takes the Array and returns true if any element of the Array is true. 
// If the Array is empty, return false.

// Python의 any function을 타입 시스템으로 구현하세요

// 배열을 사용하고 배열의 요소가 참이면 true를 반환합니다. 배열이 비어 있으면 false를 반환합니다

// 예시
type Sample1 = AnyOf< [1, "", false, [], {}] > // expected to be true.
type Sample2 = AnyOf< [0, "", false, [], {}] > // expected to be false.
/* _____________ 여기에 코드 입력 _____________ */

type AnyOf<T extends readonly any[]> = any

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

type cases = [
  Expect<Equal<AnyOf<[1, 'test', true, [1], { name: 'test' }, { 1: 'test' }]>, true>>,
  Expect<Equal<AnyOf<[1, '', false, [], {}]>, true>>,
  Expect<Equal<AnyOf<[0, 'test', false, [], {}]>, true>>,
  Expect<Equal<AnyOf<[0, '', true, [], {}]>, true>>,
  Expect<Equal<AnyOf<[0, '', false, [1], {}]>, true>>,
  Expect<Equal<AnyOf<[0, '', false, [], { name: 'test' }]>, true>>,
  Expect<Equal<AnyOf<[0, '', false, [], { 1: 'test' }]>, true>>,
  Expect<Equal<AnyOf<[0, '', false, [], { name: 'test' }, { 1: 'test' }]>, true>>,
  Expect<Equal<AnyOf<[0, '', false, [], {}, undefined, null]>, false>>,
  Expect<Equal<AnyOf<[]>, false>>,
]

 

type AnyOf<T extends readonly any[]> = T[number] extends 0 | '' | false | [] | undefined | null ? false :  true;

// 통과 못함
Expect<Equal<AnyOf<[0, '', false, [], {}, undefined, null]>, false>>

 

 통과하기 위해 { }를 조건에 추가한다면 key가 있는 객체도 모두 포함되게 된다. extends는 상속 또는 확장을 나타내고, 빈 객체는 모든 객체의 상위 개념이기 때문이다 (배열은 요소의 집합이고 빈 배열 타입으로부터 확장되지 않는다). 

 

빈 객체를 표현하는 인덱스 시그니처를 추가한다.

type AnyOf<T extends readonly any[]> = T[number] extends 0 | '' | false | [] | undefined | null | {[key : string] : never} ? false :  true;

 

 

참고자료

https://github.com/type-challenges/type-challenges/blob/main/questions/00531-medium-string-to-union/README.ko.md

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

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

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

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