본문 바로가기
Language/Typescript

타입챌린지 : 191-Append Argument (medium)

by hsloth 2023. 3. 31.

 

이 글은 제가 타입챌린지를 하면서 해석한 내용을 적는 글입니다. 틀린 내용이 있으면 댓글 달아주시면 감사하겠습니다.

 

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

 

GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

Collection of TypeScript type challenges with online judge - GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

github.com

 

해당 챌린지는 함수의 파라미터를 추가하는 타입을 만드는게 목표이다.

 

이리저리 생각을 하다 떠올린 답

// 오답
type AppendArgument<Fn, A> = Fn extends (...args: infer T) => infer R ? (x: A, ...args: T) => R : never;

Fn extends (...args: infer T) => infer R ? (x: A, ...args: T) => R : never : Fn의 인자들을 T라고 하고, 리턴 타입을 R이라고 할 수 있다면, 인자에 A를 추가해서 리턴하고 아니면 never을 리턴한다.

함수 파라미터의 위치때문에 에러가 난다... 추가할 인자가 무조건 함수의 '마지막 인자'로 들어가야 한다.

그리고 ...args 같은 경우, 함수 파라미터에서의 전개연산자는 무조건 마지막 인자에서만 사용가능하기 때문에 첫 인자로 올수가 없다.

 

그래서 다음과 같이 함수를 바꿨다.

type MyPush<T extends any[], K> = [...T, K];

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

MyPush 타입을 추가해서 배열 T 뒤에 A를 붙일 수 있도록 했다. 물론 MyPush타입 따위 안쓰고, 그냥 (...args: [...T, A]) => R : never; 로 해도 된다.

하지만... 이 경우에는 error케이스를 통과하지 못한다.

 

그래서 제네릭 인자를 받을 때 Fn이 함수라는걸 조건으로 걸어주었다.

// solution1
type MyPush<T extends any[], K> = [...T, K];

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

// solution2
type AppendArgument<Fn extends (...args: any[]) => any, A> = Fn extends (...args: infer T) => infer R ? (...args: [...T, A]) => R : never;


/* _____________ Test Cases _____________ */
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 extends Function 으로 걸어줘도 되지만... 어디서 Function은 쓰지 말라는 말을 들은거 같아서 일단 저렇게 써봤다. (물론... any도 쓰면 안되긴한데 ㅠ)