본문 바로가기

기본 타입

숫자·문자열·불리언·배열·튜플·열거형과 any·void·never·null 계열의 의미와 적절한 사용 범위를 익힙니다.

타입스크립트를 배우는 가장 중요한 목적 중 하나는 바로 타입(Type)을 활용하여 코드의 안정성과 가독성을 높이는 것입니다.

타입은 변수나 함수가 어떤 종류의 데이터를 다룰지 명확하게 정의하는 약속과 같습니다.

마치 우리가 물건을 담을 때 그 물건에 맞는 크기와 재질의 용기를 고르듯이, 코드에서도 데이터의 성격에 맞는 타입을 지정하는 것이죠.

이번 절에서는 타입스크립트에서 가장 기본적으로 제공하는 타입들을 하나씩 살펴보겠습니다.

이 기본 타입들은 여러분이 앞으로 작성하게 될 거의 모든 타입스크립트 코드의 근간이 될 것입니다.


숫자 (Number)

자바스크립트와 마찬가지로 타입스크립트의 숫자(Number) 타입은 정수와 부동소수점 숫자를 모두 포함합니다.

특별히 정수 타입이나 실수 타입이 따로 존재하지 않습니다.

이 절의 number 값은 64비트 부동소수점 형식으로 표현됩니다. 큰 정수를 다루는 별도의 bigint 타입과는 구별합니다. 타입 표기는 소문자 number, string, boolean을 사용하며, 제목의 영문 이름을 그대로 Number, String, Boolean으로 옮기지 않습니다.

let decimal: number = 6;         // 10진수
let hex: number = 0xf00d;        // 16진수
let binary: number = 0b1010;     // 2진수
let octal: number = 0o744;       // 8진수
let float: number = 10.5;        // 부동소수점

console.log(decimal); // 6
console.log(hex);     // 61453
console.log(binary);  // 10
console.log(octal);   // 484
console.log(float);   // 10.5

// 오류 예시: 문자열을 숫자에 할당할 수 없습니다.
// let notANumber: number = "hello"; // Type 'string' is not assignable to type 'number'.

위 예시에서 볼 수 있듯이, 다양한 진법의 숫자 리터럴을 number 타입에 할당할 수 있습니다.

중요한 점은 number 타입으로 선언된 변수에는 오직 숫자 값만 할당할 수 있다는 것입니다.


문자열 (String)

문자열(String) 타입은 텍스트 데이터를 나타내는 데 사용됩니다.

자바스크립트와 동일하게 작은따옴표 ('), 큰따옴표 (") 또는 백틱 (`)을 사용하여 문자열을 정의할 수 있습니다.

백틱으로 정의하는 문자열은 템플릿 리터럴(Template Literals)이라고 불리며, 여러 줄에 걸쳐 문자열을 작성하거나 표현식을 삽입할 때 유용합니다.

let courseName: string = "TypeScript for Beginners";
let instructor: string = 'Kim Coding';

// 템플릿 리터럴 사용
let introduction: string = `Hello, my name is ${instructor}.
This course is about ${courseName}.`;

console.log(courseName);    // TypeScript for Beginners
console.log(introduction);
// Hello, my name is Kim Coding.
// This course is about TypeScript for Beginners.

// 오류 예시: 숫자를 문자열에 할당할 수 없습니다.
// let notAString: string = 123; // Type 'number' is not assignable to type 'string'.

템플릿 리터럴은 문자열 내부에 ${} 구문을 사용하여 변수나 JavaScript 표현식을 삽입할 수 있어, 동적인 문자열을 훨씬 깔끔하게 구성할 수 있도록 돕습니다.


불리언 (Boolean)

불리언(Boolean) 타입은 참(true) 또는 거짓(false) 두 가지 값만을 가질 수 있습니다.

조건문이나 논리 연산에 주로 사용됩니다.

let isCompleted: boolean = false;
let hasError: boolean = true;

if (isCompleted) {
  console.log("작업이 완료되었습니다.");
} else {
  console.log("작업이 진행 중입니다."); // 이 메시지가 출력됩니다.
}

// 오류 예시: 다른 타입을 불리언에 할당할 수 없습니다.
// let isValid: boolean = 1; // Type 'number' is not assignable to type 'boolean'.
// let isReady: boolean = "yes"; // Type 'string' is not assignable to type 'boolean'.

불리언 타입은 코드의 논리 흐름을 제어하는 데 필수적이며, 타입스크립트 덕분에 true 또는 false가 아닌 다른 값이 할당되는 실수를 미리 방지할 수 있습니다.


배열 (Array)

배열은 여러 개의 값을 순서대로 저장하는 자료구조입니다.

타입스크립트에서 배열의 타입을 선언하는 방법은 두 가지가 있습니다.

요소 타입 뒤에 [] 사용: 가장 흔하게 사용되는 방법입니다.

배열이 포함할 요소들의 타입을 명시하고 그 뒤에 대괄호 []를 붙입니다.

let numbers: number[] = [1, 2, 3, 4, 5];
let fruits: string[] = ["apple", "banana", "cherry"];

// numbers.push("six"); // 오류: 'string' 형식의 인수는 'number' 형식의 매개 변수에 할당될 수 없습니다.
console.log(numbers[0]); // 1
console.log(fruits.length); // 3

위 예시처럼 number[]는 이 배열이 오직 숫자 타입의 요소만을 가질 수 있음을 의미합니다.

다른 타입의 요소를 추가하려 하면 컴파일 시 오류가 발생합니다.

제네릭 배열 타입 (Array<ElementType>): 제네릭 문법을 사용하여 Array<요소타입> 형태로 배열 타입을 정의할 수 있습니다.

이는 좀 더 명시적인 방법이며, 제네릭에 익숙해지면 더욱 강력한 타입을 정의할 때 활용됩니다.

let numbersGeneric: Array<number> = [10, 20, 30];
let namesGeneric: Array<string> = ["Alice", "Bob", "Charlie"];

console.log(numbersGeneric[1]); // 20

두 방법 모두 동일하게 배열의 타입을 정의하지만, 일반적으로 number[]와 같은 첫 번째 방식이 더 간결하여 많이 사용됩니다.

number[]는 요소의 타입을 설명하지만 길이나 해당 위치의 존재까지 보장하지는 않습니다. 다음 코드는 앞 절의 프로젝트에서 기존 예제와 섞지 말고 별도 파일로 확인하세요. 이 절에서 새로 추가한 실습은 strict: true, target: "ES2020"을 기준으로 합니다.

예시

const prices: number[] = [1200, 1800];
const first = prices[0];
if (first !== undefined) {
  console.log(first.toFixed(0)); // 1200
}
console.log(prices[2]); // undefined

인덱스는 0부터 시작하므로 두 값의 위치는 0과 1입니다. 2번 위치에는 값이 없습니다. noUncheckedIndexedAccess: true를 추가하면 일반 배열의 요소를 읽을 때 undefined 가능성도 검사하므로, first를 확인한 뒤 숫자 메서드를 사용합니다. 이 옵션은 strict: true만으로 켜지지 않습니다. 공식 옵션 설명에서 객체의 없는 키에도 적용되는 같은 원리를 확인할 수 있습니다.

직접 확인하기: 위 배열을 [900]으로만 바꿨을 때 두 출력은 무엇인가요? prices[2].toFixed(0)처럼 확인 없이 메서드를 호출하면 실제 실행에서 어떻게 될지도 먼저 적어보세요.

출력과 접근 조건 확인

첫 출력은 900, 두 번째는 undefined입니다. 마지막 질문의 메서드 호출은 undefined에서 toFixed를 읽으므로 예외가 납니다. 요소 타입이 숫자라는 약속과 그 위치에 값이 있다는 조건을 각각 확인해야 합니다.


튜플 (Tuple)

튜플(Tuple)은 특정 개수의 요소 타입을 미리 정해놓고, 그 정해진 순서와 타입에 맞춰 값을 담을 수 있는 배열의 한 종류입니다.

배열은 요소의 개수나 타입 순서에 제약이 없지만, 튜플은 정해진 형태를 유지해야 합니다.

고정된 수의 요소와 각 요소의 타입이 중요할 때 유용합니다.

// [string, number]는 첫 번째 요소는 문자열, 두 번째 요소는 숫자여야 함을 의미합니다.
let userInfo: [string, number] = ["John Doe", 30];
let rgbColor: [number, number, number] = [255, 0, 0];

console.log(userInfo[0]); // John Doe
console.log(userInfo[1]); // 30

// 오류 예시: 순서나 타입이 맞지 않습니다.
// let invalidUser: [string, number] = [30, "John Doe"]; // Type 'number' is not assignable to type 'string'.
// let incompleteInfo: [string, number] = ["Jane Doe"]; // Property '1' is missing in type '[string]' but required in type '[string, number]'.

// 요소 추가는 허용되지만, 접근 시 타입 검사가 이루어집니다.
// userInfo.push(true); // 오류: boolean은 이 튜플의 string | number 요소 타입에 맞지 않습니다.
// userInfo[2]; // Property '2' does not exist on type '[string, number]'.

위 튜플 예시에서 push(true)는 string | number 요소 타입에 맞지 않아 오류입니다. string이나 number를 push하는 것은 가변 튜플에서 가능하므로, 길이 계약을 변경하지 않으려면 그림처럼 readonly 튜플을 고려합니다.

튜플은 예를 들어 [경도, 위도]와 같이, 요소의 의미가 순서에 따라 명확하게 구분될 때 빛을 발합니다.

배열과 튜플 선택하기: 0개 이상인 상품 가격 목록과, 변경하지 않을 화면 좌표 (40, 80)을 각각 선언해 보세요. 좌표에는 어느 위치가 x와 y인지 정하고, 타입 검사 단계에서 세 번째 요소를 추가하지 못하게 하세요.

형태를 선택한 이유와 코드
const productPrices: number[] = [1200, 1800];
const screenPoint: readonly [number, number] = [40, 80];
console.log(screenPoint[0], screenPoint[1]); // 40 80
// screenPoint.push(100); // readonly 튜플에는 push가 없습니다.

목록의 개수는 달라질 수 있으므로 배열, 좌표의 두 위치에는 서로 다른 의미가 있으므로 튜플을 선택했습니다. const는 변수 재할당을 막는 선언이고 readonly는 이 타입 경로의 요소 쓰기를 제한합니다. 둘 다 런타임 객체를 자동으로 동결하는 기능은 아닙니다.


열거 (Enum)

열거(Enum)는 특정 값들의 집합에 이름을 부여하여, 코드의 가독성을 높이고 오류를 줄이는 데 사용됩니다.

예를 들어, 요일, 색상, 상태 코드 등과 같이 한정된 선택지를 표현할 때 유용합니다.

기본적으로 숫자 값을 가집니다.

// 숫자 열거 (Numeric Enum) - 기본값: 0부터 시작하여 1씩 증가
enum Direction {
  Up,    // 0
  Down,  // 1
  Left,  // 2
  Right  // 3
}

let userDirection: Direction = Direction.Up;
console.log(userDirection);         // 0
console.log(Direction.Left);        // 2
console.log(Direction[0]);          // Up (역방향 매핑도 가능)

// 특정 숫자 값 할당 가능
enum StatusCode {
  Success = 200,
  BadRequest = 400,
  Unauthorized = 401,
  NotFound = 404
}

let responseStatus: StatusCode = StatusCode.Success;
console.log(responseStatus); // 200
console.log(StatusCode.NotFound); // 404

// 문자열 열거 (String Enum) - 각 멤버에 문자열 리터럴을 할당해야 합니다.
enum HttpStatus {
  OK = "OK",
  Created = "CREATED",
  InternalServerError = "INTERNAL_SERVER_ERROR"
}

let serverStatus: HttpStatus = HttpStatus.OK;
console.log(serverStatus); // OK
console.log(HttpStatus.InternalServerError); // INTERNAL_SERVER_ERROR
// console.log(HttpStatus[0]); // 문자열 열거는 역방향 매핑을 지원하지 않습니다.

열거형을 사용하면 하드코딩된 숫자나 문자열 대신 의미 있는 이름을 사용할 수 있어 코드가 훨씬 명확해집니다.


Any (아무거나)

any 타입은 타입 검사를 비활성화할 때 사용됩니다.

어떤 종류의 값이든 할당할 수 있으며, any 타입의 변수에 대해 어떤 프로퍼티나 메서드에 접근해도 타입 검사를 하지 않습니다.

이는 자바스크립트의 유연성을 그대로 가져오지만, 타입스크립트를 사용하는 이점을 포기하는 것이므로 꼭 필요한 경우에만 제한적으로 사용해야 합니다.

let unknownValue: any = 4;
unknownValue = "hello";
unknownValue = false;

// any 타입은 어떤 속성이나 메서드에도 접근을 허용합니다.
// 실제 런타임에 오류가 발생할 수 있으니 주의해야 합니다.
unknownValue.toFixed(2); // 숫자일 때는 가능
unknownValue.toUpperCase(); // 문자열일 때는 가능
unknownValue.noSuchMethod(); // 런타임 오류 발생 가능성이 있지만, 컴파일러는 허용합니다.

let someValue: any = "이것은 문자열입니다.";
let strLength: number = (someValue as string).length; // 타입 단언 (as 키워드)

console.log(strLength); // 위 첫 toFixed 호출에서 예외가 나므로 연속 실행 시 도달하지 않습니다.

위 any 예제를 연속 실행하면 마지막 할당값이 false이므로 첫 toFixed 호출에서 실패하고 뒤의 호출·출력에는 도달하지 않습니다. 타입 검사가 통과해도 실행 성공을 보장하지 않는 예시입니다.

기존 자바스크립트 연동처럼 검사를 잠시 포기하는 경계에 any를 제한하고, 아직 모르는 외부 값은 다음 절의 unknown과 검증을 우선 고려합니다.

하지만 남용하면 타입스크립트를 사용하는 의미가 퇴색되므로 지양하는 것이 좋습니다.


Void (공허)

void 타입은 주로 함수가 어떤 값도 반환하지 않을 때 사용됩니다.

자바스크립트 함수에서 명시적으로 return 문이 없거나, return;만 있을 경우 해당 함수의 반환 타입은 void로 간주됩니다.

function warnUser(): void {
  console.log("경고: 이 함수는 어떤 값도 반환하지 않습니다.");
}

let unusable: void = undefined; // strictNullChecks: true에서 void 변수에 undefined를 할당할 수 있습니다.
// unusable = null; // strictNullChecks: true에서는 오류, false에서는 허용됩니다.

warnUser(); // 함수 실행

변수에 void 타입을 직접 할당하는 경우는 거의 없으며, 주로 함수의 반환 타입으로 사용됩니다.


Null과 Undefined

null과 undefined는 각각 자신의 이름을 타입으로 가집니다.

이들은 자바스크립트에서 "값이 없음"을 나타내는 특별한 값들입니다.

let u: undefined = undefined;
let n: null = null;

// strictNullChecks: false에서는 null과 undefined를 별도로 구분해 검사하지 않습니다.
// 즉, number, string 등에 할당될 수 있습니다.
let num: number = undefined; // strictNullChecks가 false일 경우 허용
let str: string = null;      // strictNullChecks가 false일 경우 허용

// 하지만 'strictNullChecks' 컴파일 옵션이 true일 경우,
// null과 undefined는 각각을 포함한 유니온 및 any·unknown 등에 할당 가능합니다.
// void에는 undefined가 허용되지만 null은 허용되지 않습니다.
// 이 책에서는 'strictNullChecks: true'를 권장하며, 이는 더 엄격한 타입 검사를 가능하게 합니다.

위의 느슨한 설정 예시는 strictNullChecks: false가 실제로 적용될 때를 보여 줍니다. TypeScript 6.0에서는 strict의 기본값이 true로 바뀌었지만, strictNullChecks를 명시적으로 false로 설정하면 해당 검사만 끌 수 있습니다. 기본값에 기대기보다 사용하는 TypeScript 버전과 프로젝트에 적용되는 옵션을 확인합니다.

tsconfig.json 파일에 strictNullChecks: true 옵션을 설정하면 null과 undefined가 다른 타입에 할당되는 것을 엄격하게 제한하여 잠재적인 null 참조 오류를 방지할 수 있습니다.

이 책에서는 해당 옵션을 활성화하는 것을 권장합니다. 위 코드에서 number에 undefined, string에 null을 할당하는 줄은 strictNullChecks: true에서 진단됩니다.


Never (절대)

never 타입은 절대 발생할 수 없는(도달할 수 없는) 타입을 나타냅니다.

주로 다음과 같은 경우에 사용됩니다.

  • 함수가 항상 예외를 던지거나(throw),
  • 함수가 절대 반환하지 않거나(무한 루프 등),
  • 타입 가드(Type Guards)를 사용하여 모든 가능한 경우를 처리했음을 나타낼 때.
// 1. 항상 예외를 던지는 함수
function error(message: string): never {
  throw new Error(message);
}

// 2. 무한 루프 함수
function infiniteLoop(): never {
  while (true) {
    // 이 함수는 절대 종료되지 않습니다.
  }
}

// 3. 타입 가드에서 사용
declare function getWidget(): string | number; // 문자열 또는 숫자를 반환하는 가상의 함수

function processWidget(widget: string | number) {
  if (typeof widget === "string") {
    console.log("위젯은 문자열입니다.", widget.toUpperCase());
  } else if (typeof widget === "number") {
    console.log("위젯은 숫자입니다.", widget.toFixed(2));
  } else {
    // 이 코드는 절대 실행될 수 없어야 합니다.
    // 만약 widget에 다른 타입이 추가된다면 컴파일러가 오류를 알려줍니다.
    let _exhaustiveCheck: never = widget;
  }
}

never 타입은 타입스크립트의 타입 시스템에서 특정 코드 경로가 도달 불가능함을 명시적으로 알리는 역할을 합니다.


기본 타입을 실전 코드에 적용할 때는 값의 모양, 가능한 연산, 허용할 결측값, 타입 안전성의 강도를 함께 판단해야 합니다.

원시 값, 배열과 튜플, 선택지, 결측, 함수 흐름, 불확실성을 다른 관점으로 구분합니다. 이 분류는 상속 관계나 실행 순서가 아닙니다.

TYPE / RUNTIME / CONTRACT

기본 타입은 값의 역할로 분류한다

한 값의 종류, 모음의 구조, 선택지, 결측과 함수 흐름은 서로 다른 질문입니다. 아래 분류는 상속 관계나 실행 순서가 아니라 타입을 고르는 관점입니다.

VALUE / PRIMITIVE

숫자 · 문자열 · 참과 거짓

number는 정수와 부동소수점을 함께 다룹니다. string은 텍스트, boolean은 true/false입니다. bigint는 별도의 원시 타입이며 이 절의 코드에는 등장하지 않습니다.

COLLECTION / POSITION

배열의 반복과 튜플의 위치 계약

number[]와 Array<number>는 같은 요소 계약입니다. [string,number]는 위치마다 타입을 정합니다. 가변 튜플의 push를 막으려면 readonly 튜플을 고려합니다.

CHOICE / RUNTIME

선택지 이름과 런타임 값을 구분

Direction 숫자 enum은 Up=0과 역방향 매핑을 보여줍니다. 문자열 enum에는 같은 역방향 매핑이 없습니다. literal union은 다음 절에서 다룰 정적 선택지입니다.

ABSENCE / STRICT NULL CHECKS

없을 수 있는 값을 명시한다

strictNullChecks=true에서 string|null, number|undefined처럼 결측을 표현합니다. null과 undefined를 같은 의미로 처리할지는 실제 데이터 계약에서 정합니다.

FUNCTION / CONTROL FLOW

void는 결과 계약, never는 정상 반환 불가

본문 warnUser는 정상 종료하고 결과는 void입니다. 예외 또는 무한 루프 함수는 never입니다. 모든 유니온 경우를 제거한 나머지에도 never가 쓰입니다.

CHECKING / EXTERNAL VALUE

any 허용과 unknown 검증을 구분

본문 any 값은 마지막에 false여서 toFixed에서 실패합니다. unknown은 다음 절에서 사용 전 좁히기를 요구합니다. 타입 이름만으로 실제 값의 검증이 끝나지 않습니다.

원시 값, 배열과 튜플, 선택지, 결측, 함수 흐름, 불확실성을 다른 관점으로 구분합니다. 이 분류는 상속 관계나 실행 순서가 아닙니다.


기본 타입을 고를 때 가장 자주 생기는 실수는 값의 범위, 빈 값 가능성, 컬렉션의 구조를 타입에 충분히 반영하지 않는 것입니다.

불확실한 외부 값에는 unknown과 검증을 사용합니다. 알려진 값은 형태·순서·결측·함수 흐름에 맞는 계약으로 표현하며 void와 never의 의미를 구분합니다.

TYPE / RUNTIME / CONTRACT

값의 형태와 불확실성에서 타입을 선택한다

외부에서 들어온 값인지 먼저 확인하고, 이미 파악한 값은 반복·위치·선택지·결측·함수 결과의 의미에 맞게 표현합니다. 최소 타입은 의미를 충분히 담는 계약을 뜻합니다.

값의 계약에 따른 타입 선택같은3노드2간선 선택분기이다. 값의계약을모르면unknown과런타임검증, 알면값의형태·순서·결측·함수흐름에맞는타입을고른다. 두출력은차례로실행하는단계가아니다.값의 계약을알고 있는가?아니오예unknown · 검증아직 모르는 외부 값알려진 값의 형태순서 · 결측 · 반환 의미
같은 타입 선택 분기 · 모바일같은3노드2간선 선택분기이다. 값의계약을모르면unknown과런타임검증, 알면값의형태·순서·결측·함수흐름에맞는타입을고른다. 두출력은차례로실행하는단계가아니다.값의 계약을아는가?아니오예unknown · 검증아직 모르는 외부 값알려진 값의 형태순서 · 결측 · 반환 의미

UNKNOWN / FIRST

외부 입력은 사용 전에 확인한다

unknown은 값을 받을 수 있지만 바로 속성을 읽거나 호출할 수 없습니다. 실제 타입과 필드를 확인한 뒤 그 범위에서 사용합니다.

COLLECTION / CHOICE

반복은 배열, 위치는 튜플

순서마다 의미가 다르면 tuple, 같은 요소를 반복하면 array를 고릅니다. 닫힌 상태 후보에는 literal union이나 enum의 런타임 요구를 비교합니다.

ABSENCE / FUNCTION

결측 · 반환값 · 정상 반환 불가를 구분

null/undefined는 없는 값, void는 함수 결과의 계약, never는 정상적으로 돌아오지 않는 흐름입니다. 결측과 반환 불가능성을 같은 종류로 묶지 않습니다.

LESSON / FOLLOWING TOPICS

관찰한 값이 타입 이름보다 먼저다

본문 number/string/boolean/array/tuple/enum 예제가 출발점입니다. unknown과 literal union의 상세 사용은 다음 절에서 다룹니다. any는 검사를 완화하는 선택이므로 적용 범위를 좁힙니다.

불확실한 외부 값에는 unknown과 검증을 사용합니다. 알려진 값은 형태·순서·결측·함수 흐름에 맞는 계약으로 표현하며 void와 never의 의미를 구분합니다.


기본 타입을 실제 코드에 붙일 때는 타입 이름보다 값의 성격을 먼저 살펴보는 것이 좋습니다.

단일 값인지, 컬렉션인지, 결측 가능성이 있는지, 반환값이 없는 흐름인지에 따라 선택지가 달라집니다.


여기까지 타입스크립트의 기본 타입을 정리했습니다.

이 타입들은 애플리케이션의 데이터 구조를 표현하는 출발점이며, 값의 형태와 사용 가능한 연산을 제한하는 기준이 됩니다.