[타입스크립트] 함수를 인자로 받을 때 인자의 개수가 더 적어도 되는 이유

Jongkeun Choi·2021년 2월 27일
0

의문점

전체코드:

TypeScript (forked)

타입 스크립트에서 함수를 인자로 받을 때, 인자가 한 개인 것과 두 개인 것이 있다고 하면

function inFunc1(a:number, b:number) {
	return a + b;
}

function inFunc2(a:number) {
	return a + 99;
}

function inFunc3() {
  return 765;
}

function wrapper(f: (arg1: number, arg2: number) => number) {
  return (a1: number, a2: number) => {
    return f(a1, a2);
  };
}
  let aa = wrapper(inFunc1)(1, 2);
  let bb = wrapper(inFunc2)(3, 4);
  let cc = wrapper(inFunc3)(5, 6);
  console.log(aa, bb, cc); // {aa: 3, bb: 102, cc: 765}

위의 코드는 타입스크립트 오류가 나지 않는다! 왜 그럴까? 인자를 맘대로 줄인 함수를 받아도 되나 보다.

wrapper((a, b) => 3)(5, 6);

위의 식처럼 써도 된다.

wrapper((a:string) => 3)(5, 6);

하지만 위처럼 타입이 다르면 안 된다. 그렇지만

wrapper((a, b, c: number) => 3)(5, 6);

위와 같이 인자가 2개를 초과하면 또 안 된다.

f를 콘솔에 찍어 보자.

function wrapper(f: (arg1: number, arg2: number) => number) {
  return (a1: number, a2: number) => {
    console.log(f); // 뭐가 나올까?
    return f(a1, a2);
  };
}

다음과 같이 나온다.

이미지(결과)

return f(a1, a2); 부분에서, 부족한 인자는 자동으로 잘려나간다.

왜 타입스크립트는 이런 상황을 오류로 잡지 않았을까?


깃허브 FAQ에서 찾았다.

https://github.com/microsoft/TypeScript/wiki/FAQ#why-are-functions-with-fewer-parameters-assignable-to-functions-that-take-more-parameters

... 이것은 예상되었고 바람직한 동작입니다. 첫째, 〈 교체 가능성 〉 primer를 참조하십시오. '안전하게' 나머지 파라미터들을 무시할 수 있기 때문입니다.

예를 들어

let items = [1, 2, 3];
items.forEach(arg => console.log(arg));

를 볼까요? 사실 이것은 에러가 될 수도 있습니다. 런타임에 forEach는 세 argument (value, index, array) 를 원래는 받기 때문인데, 보통 콜백은 value랑 index만 씁니다. 매우 흔한 자바스크립트 패턴이며 사용하지 않는 파라미터를 선언하는 것은 매우 귀찮은 일일 겁니다.

하지만, forEach 는 optional로 마크하고 있는데요? forEach(callback: (element?: T, index?: number, array?: T[]))

이것은 옵셔널 콜백 파라미터를 의미하는 것이 아니에요! 함수 시그니쳐는 caller의 관점에서 항상 읽어옵니다. 만약 forEach가 옵셔널 파라미터로 선언되어 있었다면, 이것은 forEach는 0개의 arguments로 호출될 수 있다는 뜻입니다.

// Invoke the provided function with 0 or 1 argument
function maybeCallWithArg(callback: (x?: number) => void) {
    if (Math.random() > 0.5) {
        callback();
    } else {
        callback(42);
    }
}

forEach는 언제나 모든 세 arguments를 콜백에게 줍니다. 당신은 indexundefined인지 체크할 필요가 없어요 - 항상 있거든요; 옵셔널이 아니에요!

현재 타입스크립트에서는 콜백 파라미터가 있어야 함을 나타내는 방법이 없습니다.

결론은 원래대로라면 value, index, array 모두 들어간 콜백함수를 받아야되는데 그런 거 없이 ()⇒console.log() 등으로 퉁쳐도 되는 편리한 의도된 설계라는 것이다.

그래서 만약 forEach를 직접 구현해 보면 왜 이게 편리한 설계였는지를 깨달을 수 있다.

function myForEach<T>(
  arr: T[],
  callbackfn: (value: T, index: number, array: T[]) => unknown,
  thisArg?: any
) {
  for (let i = 0; i < arr.length; i++) {
    callbackfn.call(thisArg ? thisArg : this, arr[i], i, arr);
  }
}

const test = ["하나", "둘", "셋"];
// 아래 네 구문 모두 허용된다.
myForEach(test, () => {
  console.log("하이도모");
});

myForEach(test, (value) => {
  console.log("하이도모" + value);
});

myForEach(test, (value, i) => {
  console.log("하이도모" + value + i);
});

myForEach(test, (value, i, arr) => {
  console.log("하이도모", arr);
});

0개의 댓글