본문 바로가기
typescript

[typescript] any ,unknown, never, void ?

by 박연호의 개발 블로그 2021. 10. 14.

길지 않은 시간동안 타입 스크립트를 사용하면서 any, void, unknown, never가 정확히 무엇이고, 언제 사용하는지 잘 몰랐습니다. 그래서 위의 키워드가 나올 때 마다 조금 찝찝했고(?) 언젠간 한번은 맘잡고 공부해야 하는 주제라고 생각했습니다.


any

[대명사] 아무(것)

[한정사] 어느, 어떤

 

any type으로 선언한 변수에는 어떠한 값도 할당할 수 있습니다. 어떤 값이 할당 되는지 전혀 문제되지 않습니다 any로 선언했으니깐요. 기존의 javascript code에는 type system이 없기 때문에 이는 typescript code에 any를 덕지덕지 붙이는 것과 다름이 없습니다. 

function stringToLowerCase(str: any) {
  return str.toLowerCase();
}

console.log(stringToLowerCase(1)); // str.toLowerCase is not a function
console.log(stringToLowerCase("TYPESCRIPT")); // typescript

stringToLowerCase함수는 매개변수로 받은 값은 문자열 메소드인 toLowerCase를 호출해 return하고 있는데,숫자값을 인자로 넘겨주게 되면 에러가 나오게 됩니다. typescript는 전혀 잘못이 없습니다. str을 any로 선언했으니깐요.

 

typescript를 사용한다면 any사용하는 것을 지양해야 하는데요, 외부 API를 사용하는 경우 응답 데이터를 참고하여 type을 만들어 type assertion을 하거나, JSON.parse 처럼 불특정 객체 역시 type을 지정해주는 것이 좋습니다.

 

어쩃든 any를 모든 변수의 타입으로 사용할 수 있습니다. 대신 그로인해 발생하는 runtime error는 어쩔 수 없습니다.


unknown

위에서 any 사용을 지양하라고 했는데요, 하지만 개발을 하다보면 stringToLowerCase의 매개변수의 타입을 정말 모르는 경우가 있습니다. 그럼에도 우리는 stringToLowerCase 함수를 작성해야 합니다. 이런 경우, 우리는 변수의 type check를 typescript가 아닌 개발자가 하게 만들 수 있습니다.

 

어쨋든 stringToLowerCase함수는 만들어야 하고 매개변수 타입을 모르겠고, 대신 runtime error는 피해야 겠고...우리가 코드상에서 직접 타입검사를 해줘야 겠습니다. 그러면 이 경우 매개변수 타입은 뭘 해야 할까요? any는 아니고, string ?, 근데 string type이 아닌 변수도 매개변수로 받을 수 있습니다.

 

위의 경우 매개변수의 type을 unknwon로 할 수 있습니다.

 

[형용사] 알려지지 않은

[명사] 미지의 것

 

저는 unknown의 기능과 이름을 정말 잘 지었다고 생각하는데요, 아직 알려지지 않았기 때문에, 우리가 실제로 코드상에서 사용할 때 검사 후 적절하게 사용해주면 됩니다.

 

unknown은 typescript 3.0에 나온 기능입니다. 공식문서에서는 unknown을 any의 type-safe한 버전이라고 소개하고 있습니다.

function stringToLowerCase(str: unknown) {
  return str.toLowerCase(); // Object is of type 'unknown'.
}

str은 unknown type으로 잡으면 에러가 나옵니다. 아마 typescript는 개발자에게 다음과 같이 말할 것입니다.

 

"toLowerCase는 문자열에 있는 메서드 아냐?, 아직 str의 type이 정확하지도 않은데 그냥 toLowerCase 사용하면 좀 위험하지 않어? 사용하기 전에 str이 문자열인지 한번 검사하고 사용해" 

function stringToLowerCase(str: unknown) {
  if (typeof str === "string") {
    return str.toLowerCase(); 
  }
  
  or
  
   const string = str as string;
   return string.toLocaleLowerCase();
}

console.log(stringToLowerCase("TYPESCRIPT")); // typescript

또한 unknown type으로 선언한 변수에는 모든 type의 값을 할당할 수 있습니다. 하지만 unknown type의 변수는 unknown, any를 제외한 다른 type의 변수에 할당할 수 없습니다

let unknown: unknown;

let any: any = unknown;
let maybe: unknown = unknown;
let number: number = unknown; // Type 'unknown' is not assignable to type 'number'.
let string: string = unknown; // Type 'unknown' is not assignable to type 'string'.
let boolean: boolean = unknown; // Type 'unknown' is not assignable to type 'boolean'.



function func<T>(any: any, never: never, T: T) {
  let x: unknown;
  x = 123;
  x = "hello";
  x = [1, 2, 3];
  x = new Error();
  x = x;
  x = any;
  x = never;
  x = T;
}

하지만 unknwon 변수를 사용할 때 먼저 unknown 변수의 타입을 검사한다면 any, unknwon이외의 type에 할당할 수 있습니다.

let maybe: unknown;
let number: number;

if (typeof maybe === "number") {
  number = maybe;
}

never

never는 절대 발생하지 않는 값의 타입을 표현하는데 사용하며 보통 에러를 발생하거나, 절대 return하지 않는(무한 루프) 함수의 return type으로 많이 사용합니다.

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

function infiniteLoop(): never {
  while (true) {}
}

function fail() { // return type이 never으로 추론됨
  return error("Something failed");
}

또한 nerver는 모든 타입의 하위타입이기 never type의 변수는 다음 모든 type의 변수에 할당할 수 습니다.

function func(params: never) {
  let number: number = params;
  let string: string = params;
  let boolean: boolean = params;
  let never: never = params;
  let nullVal: null = params;
  let undefinedVal: undefined = params;
}

 

하지만 never type의 변수에는 never type을 제외한 그 어떤 type의 변수도 할당할 수 없습니다(any 조차도 !)

let any: any;
let str: string = "str";
let num: number = 1;
let bool: boolean = true;
let nullVal: null = null;
let undefinedVal: undefined = undefined;

let never1: never = any; // Type 'any' is not assignable to type 'never'.
let never2: never = str; // Type 'string' is not assignable to type 'never'
let never3: never = num; // Type 'number' is not assignable to type 'never'.
let never4: never = nullVal; // Type 'null' is not assignable to type 'never'.
let never5: never = undefinedVal; // Type 'undefined' is not assignable to type 'never'

void

void는 전혀 어떤값을 가지지 않는 것을 의미합니다. 아마도 어떤값도 return하지 않는 함수의 reutrn type으로 종종 보았을 것입니다.

function print(str: string): void {
  console.log(str);
}

그리고 void 타입으로 변수를 선언하는 것은 딱히 유용하지 않은데, 왜냐하면 void 타입의 변수에는 null와 undefined밖에 할당하지 못하기 때문입니다. 단, --strictNullChecks 옵션이 없을때만 null를 할당할 수 있습니다.

const variable1: void = undefined;
const variable2: void = null; // OK if `--strictNullChecks` is not given