[Day4] TypeScript 타입 정리: unknown, never, void, any

soleil_lucy·2025년 6월 29일
2
post-thumbnail

Day 4 진도에 해당되는 강의를 듣고 unknown, never, void, any 타입에 대해 정리해보았습니다.

타입을 알아보기 전에 정리할 개념

any, unknown, void, never 같은 타입을 이해하려면, 먼저 타입 간의 관계를 이해할 필요가 있습니다. 이때 자주 등장 하는 개념으로 슈퍼타입, 서브타입, 업 캐스팅, 다운 캐스팅이 있습니다.

슈퍼타입 vs 서브타입

슈퍼타입과 서브타입을 그림으로 표현

슈퍼타입 (supertype)

  • 슈퍼타입은 더 일반적이고 넓은 범위의 타입입니다. 여러 서브타입들을 포괄하는, 말 그대로 “상위 타입”입니다.
  • 예를 들어 number는 1, 2, 10, 1.5, Infinity 등 모든 숫자 값을 포함할 수 있는 타입입니다.
  • 이 경우, number는 더 넓은 개념이므로 10의 슈퍼타입입니다.

서브타입 (subtype)

  • 서브타입은 슈퍼타입보다 더 구체적이고 좁은 범위의 타입입니다.
  • 10이라는 숫자 하나만 허용하는 number literal typenumber보다 범위가 좁기 때문에, 10number의 서브타입입니다.
let num1: number = 123;
const b: 10 = 10;
const c: 10 = 20; // ❌ Error: 20은 10의 서브타입이 아님 (다른 리터럴이기 때문)

업 캐스팅 vs 다운 캐스팅

타입 간의 변환을 설명할 때 자주 쓰이는 개념이 업 캐스팅(up casting)다운 캐스팅(down casting)입니다. 쉽게 말하면, 타입을 넓게 또는 좁게 바꾸는 것입니다.

업 캐스팅과 다운 캐스팅을 그림으로 표현

업 캐스팅 (up casting)

  • 서브타입의 값을 슈퍼타입 변수에 할당하는 것입니다.
  • 타입스크립트에서 보통 자동으로 허용됩니다.
const subNum: 10 = 10;
let superNum: number = 123;
superNum = subNum; // 10 -> number (업 캐스팅)

다운 캐스팅 (down casting)

  • 슈퍼타입 변수를 서브타입으로 변환하는 것을 의미합니다.
  • 이 경우에는 타입을 명시적으로 단언(assert) 해야 합니다.
const num: number = 10;
const literal: 10 = num as 10; // number -> 10 (다운 캐스팅)

unknown

More on Functions#unknown - TypeScript Handbook 보러가기

정의

unknown 타입은 정확한 타입을 알 수 없지만 일단 뭔가는 들어올 수 있는 타입입니다. 어떤 값이 있을 수는 있지만, 그 값의 타입을 알 수 없음을 나타냅니다. any 와 비슷해 보이지만, unknown 타입의 값은 타입을 확정하지 않으면 사용할 수 없습니다. 그래서 값을 사용하기 전에 타입을 검사해야 합니다.

let input: unknown = 'hello';
if(typeof input === 'string') {
	console.log(input.toUpperCase());
}

모든 타입(any, object, void, undefined, null, never)의 값의 슈퍼타입입니다. 그래서 모든 타입의 값을 할당 받을 수 있습니다. 업 캐스팅이 가능합니다.

언제 사용되는가?

  • 타입을 알 수 없지만, 안전하게 다루고 싶을 때 사용합니다.
  • 함수에서 유저 입력이나 외부 데이터(예: JSON 파싱 결과 등)을 받을 때, 사용합니다.
function handleInput(data: unknown) {
	if(typeof data === 'number'){
		console.log(data.toFixed(2));
	}
}

주의할 점

  • unknown 타입의 값은 타입 정제 없이 직접 사용할 수 없습니다.
  • 타입 좁히기(type narrowing)가 필수입니다.
  • unknown 은 타입 안전성을 유지하면서 유연하게 코딩할 수 있게 해줍니다.

never

정의

never 타입은 "절대 반환되지 않는 값"을 의미합니다.

예를 들어, 정상적으로 종료되지 않거나, 영원히 실행되거나, 오류를 던지는 함수가 대표적입니다.

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

function loopFunc(): never {
	while(true) {
		// ...
	}
}

never 타입은 아무 값도 가질 수 없는 타입이기 때문에, 모든 타입(any, unknown, object, void, undefined, null, never)의 서브타입으로 간주됩니다. 그래서 never 타입을 가진 값을 다른 타입(any, unknown, string, number, boolean 등)의 변수에 할당할 수는 있습니다.

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

// up casting
let anyVar: any = neverFunc();
let unknownVar: unknown = neverFunc();
let obj: { name: string } = neverFunc();
let voidVar: void = neverFunc();
let undefinedVar: undefined = neverFunc();
let nullVar: null = neverFunc();

let num: number = neverFunc();
let str: string = neverFunc();
let bool: boolean = neverFunc();

하지만 반대로, 다른 타입의 값을 never 타입 변수에 할당하는 것은 허용되지 않습니다.

let a: never = 'hello'; // ❌ Error!
a = 123; // ❌ Error!
a = false; // ❌ Error!
a = null; // ❌ Error!
a = undefined; // ❌ Error!
a = { name: 'Lucy' }; // ❌ Error!

언제 사용되는가?

  • 함수가 무한 루프 등으로 종료되지 않거나 예외를 던질 때 사용합니다.
  • exhaustive check(모든 경우의 수 처리)에서 사용합니다.
  • 어떤 타입의 값이라도 할당할 수 없게 하기 위해 사용합니다.
    // 아래 코드는 모두 Error가 발생합니다.
    let never1: never = 1;
    let never2: never = "string";
    let never3: never = true;

주의할 점

  • 컴파일러가 오류를 잡기 위한 힌트로 유용합니다.
  • 모든 case를 다루지 않으면 타입이 never로 남아 오류가 발생할 수 있습니다.

void

More on Functions#void - TypeScript Handbook 보러가기

정의

void는 반환값이 없다는 걸 의미하는 타입입니다. 주로 함수의 반환 타입으로 사용되며, 반환값이 없음을 명확히 합니다.

function logMessage(message: string): void {
	console.log(message);
}

언제 사용되는가?

  • 함수가 아무것도 반환하지 않을 때, 명시적으로 사용합니다.
  • 값을 반환하지 않는 함수(예: 이벤트 핸들러, 로그 함수 등)의 반환 타입 명시적으로 지정할 때 사용합니다.
  • 콜백 함수의 반환 타입으로도 자주 등장합니다.
const arr: number[] = [1, 2, 3];
const callbackFn = (value: number, index: number): void => {
  console.log(`${index + 1} 번째 요소: ${value}`);
};

arr.forEach(callbackFn);

주의할 점

  • void는 변수 타입으로는 거의 사용하지 않습니다.
  • JavaScript의 undefined와 유사하지만, 의도적으로 아무것도 반환하지 않는다는 의미를 담고 있습니다.
  • voidundefined의 슈퍼타입이라 업 캐스팅의 개념에 의해 void 함수에 return undefined는 허용됩니다.
    function logMessage(message: string): void {
    	console.log(message);
    	return undefined;
    }

any

Everyday Types - TypeScript Handbook 보러가기

정의

any 타입은 TypeScript의 타입 시스템을 우회하여, 해당 변수에 어떤 타입의 값이든 할당할 수 있도록 허용하는 타입입니다. 즉, 타입 검사를 하지 않겠다는 의미로 기존의 자바스크립트를 쓰는 것과 동일합니다.

모든 타입(never를 제외한 unknown, object, void, undefined, null 타입)의 값을 할당할 수 있는 타입으로 다운 캐스팅, 업 캐스팅이 가능합니다.

let anyVar: any;
let unknownVar: unknown;
let voidVar: void = () => { console.log('hello'); };

anyVar = unknownVar;
anyVar = { name: 'Lucy' };
anyVar = voidVar;
anyVar = undefined;
anyVar = null;

anyVar = 123;
anyVar = 'hello';
anyVar.foo();

언제 사용되는가?

  • 외부 라이브러리 등 타입 정보를 알 수 없는 값을 다룰 시, 일시적으로 타입 안정성을 포기하고 싶을 때, 사용합니다.
  • 점진적으로 타입을 적용하는 마이그레이션 과정에서 기존 자바스크립트 코드에 타입을 붙이기 어려울 때 임시로 사용합니다.

주의할 점

  • any를 사용하면 타입스크립트의 타입 시스템을 무력화 시킵니다.
  • any 사용을 남용하면, 결국 자바스크립트와 다를 바 없는 코드가 됩니다.
  • 가능한 한 사용을 피하고, 타입을 명확히 지정하는 것이 좋습니다.

회고

이번에 unknown, never, void, any 타입을 정리하면서 그동안 막연하게 알고 있던 개념들을 좀 더 확실히 이해할 수 있었습니다.

anyvoid는 평소에도 종종 보며 대략적인 용도는 알고 있었지만, unknownnever는 “이게 왜 필요하지?”, “언제 써야 하지?” 라는 의문만 남은 채 정확하게 쓰지 못했던 타입이었습니다. 이번에 강의를 듣고 정리하는 과정을 통해, 이 네 가지 타입이 각각 어떤 의미를 갖고 있고, 어떤 상황에서 사용해야 하는지를 배울 수 있었습니다.

  • any는 타입 검사를 하지 않고 어떤 값이든 허용하고 싶을 때 사용하지만, 타입 안정성을 잃게 되므로 실제 프로젝트에서는 지양하려고 합니다. 다만, JavaScript 프로젝트를 TypeScript로 점진적으로 마이그레이션할 때는 일시적으로 사용할 수 있을 것 같습니다.
  • void는 함수가 아무 값도 반환하지 않을 때 사용하는 타입으로, 이번 기회에 다시 한 번 복습할 수 있었습니다.
  • unknown은 값은 존재하지만 타입을 정확히 알 수 없을 때 사용하는 타입으로, 타입 정제를 통해 안전하게 다룰 수 있다는 점을 배울 수 있었습니다.
  • never는 무한 루프나 예외 처리처럼 함수가 정상적으로 종료되지 않는 경우에 사용하는 타입이라는 것도 알게 되었습니다.

이번 학습을 통해 앞으로 프로젝트에서 이 4가지 타입을 마주하게 되더라도, 용도나 의미를 몰라서 당황하는 일 없이 사용할 수 있을 것 같습니다.

참고 자료

profile
여행과 책을 좋아하는 개발자입니다.

2개의 댓글

comment-user-thumbnail
2025년 6월 29일

평일 중에 이 글은 보지 못했는데..! 이번주 정말 부지런하게 공부하셨군요!
덕분에 unknown, never, any 라는 신기한 타입을 알게 되었습니다.
그나마 void 반환을 간간이 쓰는데
콜백 함수의 반환 타입으로도 자주 등장한다. 정말 그렇네요!

1개의 답글