TypeScript: Basic Types Ep.4 (any, unknown, never, void)

any


any는 어떤 것이든 할 수 있다라는 의미이다.
어떤 타입이어도 상관이 없는 타입이지만, 최대한 사용하지 않는 것이 좋다.
컴파일 타임에 타입 체크가 정상적으로 이루어지지 않기 때문이다. 그래서 컴파일 옵션 중에는 any를 써야하는데 쓰지 않으면 오류를 뱉는 옵션도 있다.(nolmplicitAny)

function returnAny(message: any): any {
	console.log(message);
}

const any1 = returnAny("리턴은 아무거나");

any1.toString();

returnAny의 인자값은 어떤 값이든 들어올 수 있다. any이기 때문이다.
any1은 any이기 때문에 문자열만 사용할 수 있는 것도 사용이 가능하다.

  • any의 전파

    any는 개체를 통해 계속 전파한다. 이로인해 타입의 안정성을 잃게 된다.
    타입 안정성은 타입스크립트를 사용하는 주요 동기 중 하나이다. 그렇기에 필요하지 않은 경우에는 any를 사용하지 않도록 해야한다.

    let looselyTyped: any = {};
      
    let d = looselyTyped.a.b.c.d;
    

    looselyTyped.a.b.c.d;를 해도 Error가 나지 않는다. 그리고 이 결과는 d에 any로 들어가게 된다. 해당 any들이 개체를 통해서 전파된다는 것이다.

    • any 전파 문제점
      function leakingAny(obj: any) {
        const a = obj.num;
        const b = a + 1;
        return b;
      }
          
      const c = leakingAny({ num: 0})
      

      a도 any, b도 any, c또한 any이다. c는 number로 규정되어 나와야 하는데 말이다. 이것이 any의 전파로 인한 문제이다.

    • 누수 막기
      function leakingAny(obj: any) {
        const a: number = obj.num;
        const b = a + 1;
        return b;
      }
          
      const c = leakingAny({ num: 0})
      

      해당 문제점을 해결하고 싶다면 a에 number를 추가해준다. 이로인해 누수가 막히면서 b도 number, c도 number가 된다.

unknown


any가 가지고 있는 타입의 불안정한 요소를 해소시켜준다.
any와 짝꿍이며 any보다 타입 안정성이 있다.
unknown은 any처럼 아무거나 할당할 수 있다.
그러나, 컴파일러가 타입을 추론할 수 있게끔 타입의 유형을 좁히거나, 타입을 정해주지 않으면 다른 곳에 할당할 수 없고, 사용할 수 없다.

우리는 응용 프로그램을 작성할 때 모르는 변수의 타입을 묘사해야 할 수도 있다. 보통 모르는 변수의 타입을 any로 묘사했었다.
이러한 값은 API를 얻어서 오는 동적 콘텐츠 같은 것이다. 이러한 경우, 컴파일러와 다른 사람이 해당 코드를 읽을 때 해당 변수가 동적이기 때문에 어떤 것이든 될 수 있다는 것을 알려주는 타입이 필요했다. 기존에는 any로 사용했지만 any 이후에 오는 코드들이 any의 영향을 받으면서 코드 안정성을 해치는 문제가 생긴다.
이러한 부분을 타입적으로 강제하기 위해서 unknown을 사용하게 되었다.

  • unknown은 number로 지정할 수 없다.

    unknown의 타입을 한정시켜야 한다.

    declare const maybe: unknown;
      
    const aNumber: number = maybe;
    // Type 'unknown' is not assignable to type 'number'.
    

never


never은 보통 return에 사용된다.
never 타입은 모든 타입의 subtype이며, 모든 타입에 할당할 수 있다. 그러나 never에는 어떤 것도 할당할 수가 없다. any조차도 never에 할당할 수 없다.
never은 잘못된 타입을 넣는 실수를 막고자 할 때 사용할 수 있다.

  • throw never

    throw를 하게 되면 해당 코드 밑으로는 내려오지 않게 된다. (해당 코드 다음부터는 읽지 않는다) 즉, 해당 코드에서 함수가 끝나버린다.
    해당 함수는 throw하는 경우에 대해서 어떠한 형태도 return되지 않는 다는 것으로 never키워드를 사용한다.

    function error(message: string): never {
      throw new Error(message);
    }
    
  • never 추론

    error 함수를 사용하면서 return을 하게 되어도, never로 추론이 된다.

    function fail() {
        return error("failed")
    }
    
  • 함수 내용적 never

    해당 함수에서는 while이 계속 true이기 때문에 멈추지 않고 계속해서 루프가 돌아간다. 그렇기 때문에 return에 대해 never를 사용할 수 있다.

    function infiniteLoop(): never {
      while (true) {}
    }
    
  • 타입 입력 실수를 막기
    let a: string = 'hello';
      
    if (typeOf a !== 'string') {
      a; // never
    }
    

    a가 string인데 if문을 통해 a가 string이 아니라면(string - string = never) a의 타입은 never가 나오게 된다. a에는 아무것도 할 수 없게 되면서 잘못된 타입을 넣는 실수를 막아준다.

    type Indexable<T> = T extends string ? T & { [index: string]: any } : never;
    const ObjectIndexable = Indexable<{}>; // never
    

    Indexable이라는 제네릭(Generic)타입을 만들어서 T가 만약에 string이면 { [index: string]: any }를 만든다. 나머지인 경우에는 never로 지정한다.

  • typeOf Guard
    declare const a: string | number;
      
    if (typeOf a !== 'string') {
      a; // number
    }
    

    string + number - string = number를 나오게 해서 a의 타입으로는 number을 나오게 하는 가드가 가능하다.

void


void는 어떤 타입도 가지지 않은 빈 상태를 의미한다. 값은 없고 타입만 있어서 void라는 값은 사용할 수 가 없다.
값을 반환하지 않는 undefined를 return하는 상태에 사용한다. 그 외에는 사용할 일이 잘 없다.
return 타입이 void라는 것은 함수의 return을 가지고 무엇이든 하지 않겠다는 명시적인 표시이다.

function returnVoid(message: string) {
	console.log(message);

	return;
}

const r = returnVoid("리턴이 없다.");

해당 함수 return 타입의 추론은 void로 나오게 된다.
아무 값도 반환하지 않는 return을 넣게 되어도 해당 함수의 return 타입 추론은 void로 나온다.
r의 타입 또한 void이다.

Comments