플로우type에서 Any Type, Unknown Type, Mixed Type, Existential Type,Generics의 이야기

17543 단어 flowtypeJavaScriptflow
이 세상에서 사라졌으면 좋겠어.
저는 그렇게 생각합니다. 하지만 플로우type에서any와 비슷한 용도가 있지만 어떤 유형의 사람들은 어떻게 구분해서 사용해야 할지 잘 몰라서 정보를 정리해 보려고 합니다.

Any Type

var anyType: any;
AnyType$Tainted 이외의 모든 유형은 받아들일 수 있으며 모든 유형이 변환될 수 있는 특수한 유형이 됩니다.
간단하게 애니 타입을 이용하면 플로우type의 정태형 검사는 하지 않는다.
var anyType: any = 1;
anyType.toFixed();
anyType.replace('', ''); // Not Erorr
예를 들어 상술한 존재하지 않는 방법을 호출할 수 없을 때 오류가 발생하는 코드를 통해 유형 검사를 할 수 없어 실행할 때의 오류를 초래한다.
금형을 검사하기 위해 플로우type을 가져왔지만 AnyType이 유형 검사를 하지 않는 불가사의한 상황에 빠졌기 때문에 기본적으로 명확한 이유가 없는 상황에서 AnyType을 사용해서는 안 된다.
그렇다면 AnyType은 언제 사용해야 할까요?
저는 다음 두 가지를 AnyType을 이용할 때의 판단으로 삼겠습니다.
  • 유형 정의가 존재하지 않는 라이브러리를 이용하여 스스로 유형 정의를 쓰는 비용이 높을 때
  • 유형 정의에 따라 설치를 강제로 하려는 경우
  • 1. 예를 들어 개발 공정을 확보하지 못하고 변경 빈도가 높으며 스스로 작성하고 금형의 정의를 유지하는 원가가 높고 수지가 맞지 않을 때.
    뭐, 그런 일도 있지.이런 느낌은 이 경우에도 후술한 믹스드 타입이나 Type Refinements을 최대한 사용해 회피한다.
    2. 도저히 실장과 정의를 결합할 수 없다...이럴 때는 최종 수단으로 쓰인다.
    // @flow
    const crypto = require('crypto');
    
    let cache: {[key: string]: Function} = {};
    
    module.exports = function memorize<S: Function, T: S>(f: S): T {
      let fString = f.toString();
      return (function() {
        let aString = JSON.stringify(Array.from(arguments));
        let key = hash(`${fString}:${aString}`);
    
        if (cache[key] !== undefined) {
          return cache[key];
        } else {
          return cache[key] = f.apply(this, arguments);
        }
      }: any);
    };
    
    function hash(plaintext: string): string {
      let hashsum = crypto.createHash('sha256');
      hashsum.update(plaintext);
      return hashsum.digest('hex');
    }
    
    위에 쓴 코드는 형식 정의와 일치하지 않습니다. AnyType을 사용하지 마십시오. 정의와 일치하도록 합니다.(좋은 글씨가 있으면 알려주세요)
    이때 AnyType 사용 여부를 판단하는 기준으로 함수 내에서 AnyType으로 묶어 단원 테스트를 강화하여 이전과 같은 행동을 하도록 보증하고 이 두 가지를 만족시키면 사용한다.
    AnyType은 편리한 물건이지만 다른 대용법은 없는지 잘 생각해 보고 활용하세요.

    Unknown Type


    AnyType과 동일한 행동을 하는 Unknown Type입니다.(Unknown Type은 정식 명칭flow type-at-pos이 아니며, (unknown)로 표시되어 있어 쉽게 이렇게 부른다)
    플로우type이 추론을 할 수 없을 때 발생하는 유형입니다.
    class MemoryStore<O: Object> {
      _data: $Shape<O>;
    
      constructor(data: $Shape<O>) {
        this._data = data;
      }
    
      set<K>(key: K, value: $ElementType<O, K>): void {
        this._data[key] = value; // value is Unknown
      }
    }
    
    예를 들어 위 코드set의 매개변수로 전달value하는 것은 Unknown Type과 같습니다.
    이것은 flow check에서 검출될 수 없으며 flow type-at-pos에서 해당하는 위치를 검사하거나 flow coverage에서 덮어쓸 수 없음을 확인할 수 있습니다.
    간단하게 검사하려면 Atom의 Nuclideflow coverage를 사용하면 코드에 결과를 표시할 수 있습니다.

    의도적으로 쓴 AnyType에도 경고가 표시되기 때문에 다소 번거로우며, 무심코 Unknown Type으로 변하는 것을 방지하기 위해 개발할 때 항상 효과가 있는 것이 좋다.
    솔직히 Unknown Type이 생기면 틀릴 수 있는 옵션을 원해요.

    Mixed Type


    Any Type을 교체할 때 가장 먼저 떠오르는 것이 바로 이 Mixed Type이다.
    Mixed Type은 AnyType과 마찬가지로 $Tainted를 제외한 모든 유형은 받아들일 수 있지만 사용형은 Type Refinements를 강제로 적용한다.
    var mixedType: mixed = 1;
    mixedType.toFixed(); // Error
    mixedType.replace('', ''); // Erorr
    
    AnyType과 같은 예는 대입할 수 있지만 방법이 호출되지 않았습니다.
    하면, 만약, 만약...
    if (typeof mixedType === 'number') {
      mixedType.toFixed();
    }
    
    이렇게 Type Refinements를 수행합니다.
    이렇게 Type Refinements를 강제하면 Any Type처럼 실행할 때 오류가 발생하는 것을 방지할 수 있다는 것이 Mixed Type의 가장 큰 장점이다.
    대부분의 AnyType은 Mixed Type으로 변환하여 더 안전한 코드를 만들 수 있습니다.
    AnyType을 사용하려면 이 Mixed Type을 대용할 수 있는지 생각해 보십시오.

    Existential Type


    flowtype에는 Existential Type을 강제로 추론하는 유형이 있습니다.
    var existentialType: * = 1;
    existentialType.toFixed();
    existentialType.replace('', ''); // Error
    
    위와 같은 경우 existentialType가 대입1했기 때문에 number형으로 식별할 수 있다.(단, 위의 예에서 Existential Type을 사용하지 않더라도 추론할 것이기 때문에 고마울 것이 없다.)
    스스로 Existential Type을 사용할 때는 문법적으로 틀이 필요하지만 써지지 않거나 번거로울 때 자주 사용한다.
    declare class Foo<A, B: $Keys<A>> {
      constructor(data: A): void;
    }
    
    declare var foo: Foo<{}, *>;
    
    위에서 말한 바와 같이Generics를 통해 여러 종류의 매개 변수를 얻어야 하지만, 하나가 지나면 결정할 수 있다
    class Foo {
      data: *;
    
      constructor() {
        this.data = { ... };
      }
    }
    
    이렇게 속성을 초기화할 때 유형을 정의해야 할 때 생략하려고 할 때 사용하는 경우가 많다.
    Any Type과 달리 Existential Type은 유실형 정보(추론만 가능하다면)를 잃어버리지 않기 때문에 사용이 편리하다.
    그러나 속성 초기화의 예 등에서 초기화할 때의 typo에서 키 이름, 값 등을 잘못하면 구상한 유형과 다른 유형이 될 수 있다.
    따라서 적당한 유형 정의를 미리 준비하는 것이 좋다.
    일반적으로 금형을 사용하기 위해서는 다른 수단이 존재하기 때문에 그것을 사용하는 경우가 많아질 것이라고 생각합니다.
    이럴 때 Existential Type을 사용하지 않으면 표현이 불가능하다면 정보를 제공해 주십시오.

    Generics


    학급과 함수에서 어떤 값이든 받아들이고 싶을 때Generics를 많이 사용한다.
    function arrayWrapper(value: any): Array<any> {
      return [value]
    }
    
    위에서 말한 바와 같이 AnyType으로 표현할 수 있지만 이렇게 하면 형식적인 정보를 잃게 된다
    function arrayWrapper<T>(value: T): Array<T> {
      return [value]
    }
    
    이렇게 Generics를 사용하는 경우가 많습니다.
    이번 예처럼 부작용이 없는 단순 함수로 Generics를 사용하면 설치는 하나만 쓸 수 있기 때문에 금형으로 묶어 설치할 수 있다는 장점도 있다.
    그나저나 플로우 type 문서에서Existential Type의 예도 마찬가지로Generics로 쓰는 것이 스마트하다.
    function makeParamStore<T, P: ParamStore<T>>(storeClass: Class<P>, data: T): P {
      return new storeClass(data);
    }
    (makeParamStore(ParamStore, 1): ParamStore<number>);
    (makeParamStore(ParamStore, 1): ParamStore<boolean>); // Error
    
    확실히 문서에서 보듯이 Existential Type도 기대하는 반환값의 유형이다. 예를 들어 return new storeClass(data);return 'foo';이면 함수의 유형 검사는 틀리지 않고 통과된다.
    이를 방지하기 위해서는 Generics를 사용하여 설치 오류를 감지하는 것이 중요합니다.
    나는 이런 형식으로 실제적인 생각을 속박하는 것을 매우 좋아한다. 왜냐하면 나는 동태적인 모형 언어만 접하기 때문에 이 생각을 들을 때 전혀 생각하지 못했다!!이런 충격성.
    어떻게 해야 틀로 실장을 묶을 수 있는지 깨닫기 시작하면 정음형 언어가 더 흥미로워질 수 있으니 추천한다.

    무료


    그러고 보니 이전 방법의 반환치void가 생각났는데 any식의 행동void을 하는 것이 무서웠다.
    fixes type check in todomvc-flow by k-kinzal · Pull Request #102 · almin/almin
    확실히 @flow를 넣는 것을 잊었을 때 형식적인 정의가 아니라 실제 추론이 우선void된 일의 검사를 하지 않았다.void 나쁘지 않지만 잊어버리면@flow 이런 의도 없는 행동을 할 수 있으니 주의가 필요하다.
    @flow의 유무는 eslint 검사를 통해 - Qiita
    eslint로 유무 검사@flow를 할 수 있기 때문에flowtype을 개발에서 유효하다고 추천합니다.

    총결산


    나는 AnyType을 다시 생각해 봤는데, AnyType을 사용하고 싶을 때는 기본적으로 타협할 때였다.
    일반적으로 플로우 type으로 일부러 동적 자바스크립트에서 정적 세계로 들어가 파괴하는 모습을 보이는 것은 이상하다.
    빨리 애니타입을 쓰면 밤길에 찔린 세상이 됐으면 좋겠다.

    좋은 웹페이지 즐겨찾기