openlang

Typescript 호출 가능 타입의 계약

Typescript는 함수 정의가 인터페이스 시그니처와 다른데도 받아 주면서, 왜 그 함수를 실제로 호출할 때는 문제를 보고하나요.

  interface ITextRenderer {
      (text: string, capitalize : boolean): string;
  };

  //No compiler diagnostic at this assignment.
  var capitalRenderer: ITextRenderer = function (text: string) {
      return text.capitalizeCase();
  }  

  capitalRenderer("demo"); //A diagnostic occurs at this call instead.
영어에서 번역원문 보기

이어지는 대화 2

서로의 근거를 따라
  1. 고유 링크

    인터페이스는 구현 함수를 호출하는 쪽에서 필수 인수인 text와 capitalize를 모두 전달하도록 요구합니다.

    TypeScript는 JavaScript가 쓰지 않는 추가 인수를 받아도 괜찮다는 걸 알기 때문에 구현에서 그 인수를 생략하도록 허용합니다.

    왜 괜찮을까요? 호출하는 코드를 바꾸지 않고 구현을 서로 교체할 수 있기 때문입니다.

    예를 들어 아래의 ITextRenderer 구현 둘 중 어느 것을 넣어도 동작합니다.

      interface ITextRenderer {
          (text: string, capitalize: boolean): string;
      };
    
      var capitalRenderer: ITextRenderer = function (text: string) {
          return text.capitalizeCase();
      }
    
      var flexibleRenderer: ITextRenderer = function (text: string, capitalize: boolean) {
          if (capitalize) {
              return text.capitalizeCase();
          }
    
          return text.toLowerCase();
      }
    
      // Either implementation can go here
      //var renderer = capitalRenderer;
      var renderer = flexibleRenderer;
    
      renderer("demo", true);

    TypeScript가 이를 허용하지 않았다면 capitalRenderer에 본문에서 전혀 사용하지 않는 capitalize 매개변수를 넣어야 해서 함수가 더 읽기 어려워졌을 겁니다.

    영어에서 번역원문 보기
  2. 고유 링크

    채택된 설명은 오해의 소지가 있습니다. 실제 운영에서는 애초에 하지 않는 타입 검사를 피하려고 운영 코드에서 추가 인수를 이리저리 넣어 보라는 뜻으로 들릴 수 있으니까요.

    타입 검사에서 인수 개수가 다른 형태들을 허용하려면, 그 대안들을 오버로드로 명시적으로 선언하세요. 예를 들면

      interface ITextRenderer {
          (text: string, capitalize: boolean): string;
          (text: string): string;
      };
    
      var capitalRenderer: ITextRenderer = function (text: string) {
          return text.capitalizeCase();
      }  
    
      capitalRenderer("demo"); // selects the single-parameter signature
      capitalRenderer("demo","false"); // selects the double-parameter signature

    그래도 TypeScript가 함수 정의와 호출에서 왜 다르게 검사하는지는 여전히 설명되지 않습니다. 이를 설명하는 공식 문서가 없다면 TypeScript 자체가 TypeScript / JavaScript로 구현된 데서 기술적 제약이 생긴다고 추측하고, Go로 구현되는 TypeScript 7에서는 바뀌는지 지켜볼 수 있겠죠: https://github.com/microsoft/typescript-go?tab=readme-ov-file#typescript-7

    영어에서 번역원문 보기

같은 질문의 다른 갈래

  1. 문자열 배열을 값으로 갖는 TypeScript Map 초기화

    각 항목이 문자열 배열을 담는 Map을 만들고 있어요. 타입 선언과 초기화를 한 문장으로 같이 하려면 어떻게 하나요? 제 (단순한) 시도는 이랬어요: 그랬더니 이렇게 나와요:

    답글 2최근 활동 typescript
  2. Solidity에서 address를 uint256으로 변환할 때 실패해요

    struct를 담은 배열을 만들고, 그 배열에서 각 계정의 값을 얻으려고 했어요. 그런데 msg.sender를 담은 address 변수를 그 배열에 전달하니 타입이 uint256으로 변환되지 않는 듯해서 실패했어요. 어떻게…

    답글 3최근 활동 solidity
  3. 읽기 전용 List<Int>는 스냅샷인가요?

    List<Int>는 읽기 전용 뷰예요. 스냅숏이 아니에요. 그 참조로 어떤 연산을 호출할 수 있는지 알려 줄 뿐, 뒤에 있는 컬렉션이 그대로 남는다고 약속하지는 않아요. val owned = mutableListOf(2, 4…

    답글 4최근 활동 kotlin