Study/Typescript

[TypeScript 정리] Section 02 — 타입스크립트 기본 완벽 정리

the.Dev.Cat 2025. 11. 7. 00:33

 

이 글은 이정환 강사님의 한 입 크기로 잘라먹는 타입스크립트(TypeScript) 강의를 참고하여 개념을 정리했습니다!

0. 기본 타입이란?

TypeScript의 기본 타입(Basic Types)은 언어가 기본적으로 제공하는 내장 타입을 의미합니다.

  • number, string, boolean, null, undefined 등이 있고
  • any, void, never, unknown처럼 TypeScript에서만 제공되는 타입들도 있습니다.

이 타입들은 단순히 나열된 것이 아니라, 서로 부모–자식 관계를 이루는 계층 구조를 가지고 있습니다.

 


 

1. 원시 타입과 리터럴 타입

원시 타입 (Primitive Type)

  • 한 번에 하나의 값만 담을 수 있는 기본 타입입니다.
  • 객체나 배열처럼 여러 값을 동시에 저장하는 구조가 아닙니다.
  • number, string, boolean 등이 포함됩니다.

타입 주석(Type Annotation)

변수 뒤에 :을 붙여 타입을 명시합니다.

let num: number = 123;

number

number 타입은 정수, 소수, 음수, Infinity, NaN 등 숫자 관련 모든 값을 포함합니다.

let num1: number = 123;
let num2: number = -123;
let num3: number = 0.123;
let num4: number = -0.123;
let num5: number = Infinity;
let num6: number = -Infinity;
let num7: number = NaN; // Not a Number
  • number 타입 변수에는 숫자만 할당할 수 있고, 문자열 전용 메서드는 사용할 수 없습니다.
num1 = 'hello';      // 타입 불일치
num1.toUpperCase();  // number 타입에 없는 메서드

 

string

문자열은 ", ', 백틱(\)을 모두 사용할 수 있으며, 템플릿 리터럴(${})도 가능합니다.

let str1: string = "hello";
let str2: string = 'hello';
let str3: string = hello;
let str4: string = hello ${str1};

 

boolean

  • 참(true), 거짓(false) 두 가지 값만 가질 수 있습니다.
  • 흔히 조건문이나 비교 연산 결과를 저장할 때 사용합니다.
let bool1: boolean = true;
let bool2: boolean = false;

 

null

  • null 타입은 값이 없음을 명시적으로 표현할 때 사용합니다.
  • 오직 null 하나의 값만 포함합니다.
let null1: null = null;

 

undefined

  • undefined 타입은 값이 정의되지 않았음을 의미한다.
  • null 타입과 마찬가지로 오직 undefined 하나의 값만 가진다.
  • let undefined : undefined = undefined;

 

null과 undefined 비교

  • 공통점
    • 둘 다 “값이 없다”는 걸 표현하는 타입입니다.
    • 실제 데이터가 존재하지 않는 상태를 나타낸다는 점에서 같습니다.
  • 차이점 (개발자의 의도)
    • null : 개발자가 직접 비워둔 값. “지금은 비어있지만 나중에 채울 거야”라는 의도가 담겨 있습니다.
    • undefined : 자동으로 비워진 값. 변수는 만들어졌지만 아직 어떤 값도 안 들어간 상태입니다.

 

리터럴 타입(Literal Type)

  • 값 그 자체가 타입이 되는 형태를 리터럴 타입이라고 합니다.
  • 딱 하나의 값만 허용하는 타입 입니다.
let numA: 10 = 10;
  • 리터럴 타입은 숫자뿐 아니라 문자열, 불리언 값에도 적용됩니다.
let strA: "hello" = "hello";
let boolA: true = true;
let boolB: false = false;
  • "hello"라는 특정 문자열, truefalse라는 특정 불리언 값 자체를 하나의 고정된 타입으로 다루는 개념입니다.

 


 

2. 배열과 튜플

배열(Array)

  • 같은 타입의 값들을 묶어서 저장하는 자료형
  • 배열 타입을 정의하는 기본적인 방법
    • 변수 이름 뒤에 :로 타입 주석을 쓰고, 요소타입[] 형태로 정의합니다.
let strArr: string[] = ["hello", "im", "winterlood"];
let numArr: number[] = [1, 2, 3];
  • 또는 제네릭 문법으로 같은 의미로 표현할 수도 있습니다.
let boolArr: Array<boolean> = [true, false, true];
  • 두 방법은 완전히 동일하지만, 대부분은 타입[] 방식이 간결해서 더 자주 쓰입니다다!
  • 같은 타입의 값들을 묶어 저장합니다.
let strArr: string[] = ["a", "b", "c"]; 
let boolArr: Array = [true, false];
  • number[]Array는 동일한 의미이지만, 전자가 더 일반적입니다.

 

보충 개념 : 제네릭(Generic)

  • 타입을 변수처럼 사용하는 문법입니다.
  • 함수나 클래스, 타입을 만들 때 구체적인 타입을 미리 정하지 않고, 사용할 때 원하는 타입을 전달해서 재사용성을 높이는 방법
function printArray(arr: T[]): void {  
console.log(arr);  
}

printArray([1, 2, 3]);  
printArray(["a", "b", "c"]);
  • 여기서 <T>가 바로 제네릭 타입 변수
  • 이 함수는 배열을 출력하지만, 배열 안의 타입이 string일 수도, number일 수도 있습니다다.
  • 그래서 <T>라는 타입 자리를 미리 만들어 두고, 함수를 쓸 때 원하는 타입을 넣습니다.
printArray<number>([1, 2, 3]);     // number[]
printArray<string>(["a", "b", "c"]); // string[]
  • 핵심 : 하나의 함수로 여러 타입을 처리!

 

여러 타입을 허용하는 배열

유니온 타입(|)을 사용해 여러 타입의 요소를 허용할 수 있습니다.

let multiArr: (number | string)[] = [1, "hello"];
  • 괄호 안의 number | string은 배열의 각 요소가 숫자이거나 문자열일 수 있음을 의미합니다.
    → 즉, 여러 타입 중 하나를 허용하는 타입이 됩니다.

 

다차원 배열

  • []를 연달아 쓰면 다차원 배열도 표현할 수 있습니다.
let doubleArr: number[][] = [  
[1, 2, 3],  
[4, 5],  
];

 

튜플(Tuple)

  • 배열과 비슷하지만 길이와 각 요소의 타입이 고정된 배열입니다.
let tup1: [number, string, boolean] = [1, "hello", true];
  • 첫 번째 요소는 숫자, 두 번째는 문자열, 세 번째는 불리언처럼 각 자리에 어떤 타입이 올지 정확히 정해진 배열입니다.

 

튜플을 사용할 때 주의점

  • 튜플도 결국 자바스크립트에서는 배열로 변환되기 때문에,pushpop 같은 배열 메서드를 사용하면 길이가 변할 수 있습니다.
let tup1: [number, number] = [1, 2]; 

tup1.push(3); // 가능하지만 의도치 않은 결과
  • 튜플을 쓸 때는 배열 메서드로 값을 추가하거나 삭제하지 않도록 주의해야 합니다.

 

튜플 사용 예시

  • 튜플을 사용하면, 배열의 구조(요소 타입과 순서) 까지 타입으로 안전하게 관리할 수 있습니다!
  • 예시 : 회원 정보를 이름과 ID로 구성된 배열로 관리
const users = [
  ["개냥이", 1],
  ["이아무개", 2],
  ["김아무개", 3],
  ["박아무개", 4],
];
  • 이때 누군가 순서를 잘못 입력하면, 문제가 발생합니다.
["조아무개", 5]; // 올바름 
[5, "조아무개"]; // 순서 잘못됨
  • 자바스크립트는 이런 실수를 잡지 못하지만, TypeScript에서는 튜플 타입으로 지정해 방지할 수 있습니다.
const users: [string, number][] = [ 
  ["개냥이", 1], 
  ["이아무개", 2], 
  [5, "조아무개"], // 오류 
];

 


 

3. 객체

객체 타입으로 정의하는 방법

  • TypeScript에서 객체 타입을 정의하는 방법은 크게 두 가지가 있습니다!

1. object 타입

let user: object = {  
id: 1,  
name: "개냥이",  
};
  • 이렇게 하면 user가 객체라는 건 알려주지만, 객체 안에 어떤 프로퍼티가 있는지TypeScript가 알 수 없습니다.
  • 그래서 아래처럼 프로퍼티에 접근하면 오류가 발생합니다.
  • user.id; // 오류: 'object' 타입에는 'id' 프로퍼티가 없습니다.
  • 결론 :object 타입은 “이건 객체임” 정도만 알려주고 구체적인 구조 정보는 전혀 제공하지 않습니다.

 

2. 객체 리터럴 타입(Object Literal Type)

  • 다음처럼 객체 리터럴 타입으로 정의할 수 있습니다.
let user: {  
id: number;  
name: string;  
} = {  
id: 1,  
name: "개냥이",  
};

user.id; // 정상 작동
  • 이제 user가 어떤 프로퍼티를 갖고 있는지 TypeScript가 정확히 알 수 있습니다.
    ->user.id, user.name처럼 안전하게 접근할 수 있습니다.

 

구조적 타입 시스템(Structural Type System)

  • TypeScript는 객체의 구조(프로퍼티 구성)로 타입을 판단합니다.
    • “이 객체에 name과 color가 있다면, 이건 강아지 타입이야” 라는 식으로 판단합니다.
  • let dog: { name: string; color: string; } = { name: "돌돌이", color: "brown", };

 

특수한 프로퍼티 정의하기

  • 객체 타입을 정의할 때, 프로퍼티를 선택적으로(optional) 또는 읽기 전용(readonly) 으로 지정할 수도 있습니다.

1. 선택적 프로퍼티(Optional Property)

  • 프로퍼티 이름 뒤에 ?를 붙이면, 그 프로퍼티는 있어도 되고 없어도 되는 선택적 속성이 됩니다.
let user: {
  id?: number;
  name: string;
  } = {
  name: "홍길동" 
};
  • 단, 존재할 경우엔 정해진 타입만 허용됩니다.
user = {
  id: "id", // 오류 (id는 number 타입이어야 한다)
  name: "홍길동",
};

2. 읽기 전용 프로퍼티(Readonly)

  • readonly를 붙이면 한 번 정해진 값은 수정할 수 없습니다.
  • 이러한 방식으로 실수로 데이터를 바꾸는 걸 방지할 수 있습니다.
let user: { readonly name: string; } = { name: "개냥이", };

user.name = "홍길동"; // 오류

 


 

4. 타입 별칭(Type Alias)과 인덱스 시그니처(Index Signature)

타입 별칭

  • 긴 타입을 간단한 이름으로 저장해두는 문법
  • 변수 선언하듯 type 키워드로 새 타입을 정의
  • 장점 : 반복되는 구조를 짧게 쓰고, 가독성도 훨씬 좋아집니다!
type User = {  
  id: number;  
  name: string;  
  nickname: string;  
  birth: string;  
};
  • 이제 User라는 이름만 써도 이 객체 타입 전체를 대체할 수 있습니다.
let user: User = { ... };
let user2: User = { ... };

 

주의 : 타입 별칭 이름은 중복 금지!

  • 변수처럼 같은 스코프(범위) 안에서는 같은 이름 중복 선언 불가합니다.
type User = { id: number };

function test() {
  type User = string; // 가능
}
  • 결론 : 함수 안의 User는 바깥의 User와 별개니까 괜찮습니다.

 

타입 별칭은 컴파일하면 사라짐

  • 타입 별칭은 개발 중 타입 검사 용도로만 사용됩니다.
  • 컴파일 후, 자바스크립트 결과물에는 전혀 남지 않습니다.
type User = { id: number };
  • 이 코드는 컴파일 후 완전히 사라지고, 실제 JS 코드에는 아무 영향도 없다.

 

인덱스 시그니처

  • 객체의 key/value 타입을 유연하게 지정할 수 있게 해주는 문법입니다.
  • 예시 : 전 세계 국가 코드를 담은 객체
type CountryCodes = {
  [key: string]: string;
};

let countryCodes: CountryCodes = {
  Korea: "ko",
  UnitedState: "us",
  Brazil: "br",
};
  • 모든 keystring, 모든 valuestring이라는 의미
  • 국가가 10개든, 100개든, 일일이 다 정의하지 않아도 됩니다!
  • 숫자 값이 필요한 경우엔?
type CountryNumberCodes = {
  [key: string]: number; 
};

let codes: CountryNumberCodes = {
  Korea: 82,
  Japan: 81,
};

 

인덱스 시그니쳐 + 필수 프로퍼티 같이 쓰기

  • 인덱스 시그니처에 더해서 특정 key는 무조건 있어야 한다고 명시할 수 있다.
type CountryNumberCodes = {
    Korea: number; // 필수  
};
  • 주의점 : 단, 인덱스 시그니처의 value 타입과 필수 프로퍼티의 타입이 달라지면 오류가 발생
type CountryNumberCodes = {
  [key: string]: number;
  Korea: string; // 오류
};

 


 

5. 열거형(Enum)

  • enumTypeScript에서만 제공되는 특별한 타입
  • 여러 개의 이름 있는 상수 값을 한 곳에 모아 관리할 때 사용합니다.
  • JS에는 없는 문법이지만, 컴파일하면 실제 JS 객체로 바뀝니다.기본 문법
  • 예시
enum Role {
  ADMIN,
  USER,
  GUEST,
}
  • 이렇게 선언하면 Role.ADMIN, Role.USER, Role.GUEST 세 개의 상수를 가진 enum이 만들어집니다.
  • 기본적으로 값은 0부터 자동으로 1씩 증가합니다.
    • Role.ADMIN → 0
    • Role.USER → 1
    • Role.GUEST → 2
  • 직접 숫자 값 지정하기
enum Role {
  ADMIN = 10,
  USER,   // 11 (자동 증가)
  GUEST,  // 12
}
  • 맨 위 값만 직접 지정하면, 그 아래 멤버들은 자동으로 +1 씩 증가한 숫자가 할당된다.
  • 이런 형태를 숫자형 enum (Numeric Enum) 이라고 부른다.
  • 문자열 열거형 (String Enum)
    • enum의 값은 꼭 숫자일 필요는 없다. 문자열도 지정할 수 있습니다.
    • 언어 코드 같은 고정 문자열을 안전하게 관리할 때 자주 씁니다.
enum Language {
  korean = "ko",
  english = "en",
}
  • 실제 사용 예시
    • enum을 사용하면 숫자나 문자열 대신 의미 있는 이름으로 값들을 다룰 수 있어서 코드가 훨씬 읽기 쉬워집니다.
enum Role {
  ADMIN,
  USER,
  GUEST,
}

enum Language {
  korean = "ko",
  english = "en",
}

const user1 = {
  name: "이정환",
  role: Role.ADMIN,       // 0
  language: Language.korean, // "ko"
};
  • 컴파일 결과
    • enumTypeScript에서 컴파일될 때 사라지지 않고, JS 객체 형태로 변환됩니다.
    • 그래서 enum을 타입뿐 아니라 값으로도 사용할 수 있습니다!
var Role;
(function (Role) {
  Role[Role["ADMIN"] = 0] = "ADMIN";
  Role[Role["USER"] = 1] = "USER";
  Role[Role["GUEST"] = 2] = "GUEST";
})(Role || (Role = {}));
  • 이런 식으로 양방향 매핑 객체로 변환됩다.
    • Role.ADMIN0
    • Role[0]"ADMIN"

 


 

6. any와 unknown

any 타입

  • TypeScript에서만 제공되는 타입 검사를 아예 안 받는 치트키 타입.
  • any 타입을 쓰면 TypeScript의 모든 타입 규칙이 무시됩니다.
let anyVar: any = 10;

anyVar = "hello";
anyVar = true;
anyVar = {};
anyVar.toUpperCase();
anyVar.toFixed();
anyVar.a;
  • 어떤 값이든 담을 수 있고, 어떤 메서드를 호출해도 오류가 나지 않습니다.
  • any는 모든 타입의 슈퍼타입이라서 다른 어떤 타입 변수에도 자유롭게 할당됩니다.
let anyVar: any = "hello";
let num: number = 10;

num = anyVar; // 가능 <- (타입 검사 통과)

 

any 타입의 문제점

  • TypeScript타입 안전성을 완전히 잃게 되고, 런타임 오류 위험이 커집니다.
  • any는 진짜 어쩔 수 없는 경우를 제외하고는 사용하지 않는 게 원칙입니다!

 

unknown 타입

  • unknownany처럼 모든 타입의 값을 담을 수는 있습니다.
  • 차이점 : any와는 다르게, 아무 곳에도 자유롭게 할당할 수는 없는 보다 안전한 타입입니다!
let unknownVar: unknown;

unknownVar = "";
unknownVar = 1;
unknownVar = () => {};
  • 어떤 값이든 받을 수 있지만, 그걸 다른 타입 변수에 넣을 때는 오류가 발생합니다.
let num: number = 10;

num = unknownVar; // 오류
  • unknown 타입의 값은 연산을 하거나 메서드를 직접 호출할 수도 없습니다.
unknownVar * 2; // 오류
unknownVar.toUpperCase(); // 오류
  • 결론
    • unknown“받을 수는 있지만 쓸 수는 없는 타입” 입니다.
    • 이 값이 어떤 타입인지 확실히 보장되기 전까지는 사용할 수 없습니다.

 

타입 좁히기(Type Narrowing)

  • 만약 unknown 값을 실제로 사용하려면, 조건문으로 타입을 먼저 확인해줘야 합니다.
  • 이런 식으로 조건을 통해 타입을 좁혀주는 과정을 타입 좁히기(Type Narrowing)라고 합니다.
if (typeof unknownVar === "number") {
  unknownVar * 2; // 이제 number로 인식됨
}

 


 

7. void와 never

void 타입

  • void“아무 값도 없다” 는 걸 의미하는 타입입니다.
  • 주로 함수가 아무 값도 반환하지 않을 때 사용합니다.
function func2(): void {
  console.log("hello");
}
- 이 함수는 `console.log`만 실행하고 아무것도 반환하지 않기 때문에 반환 타입을 void로 지정한다.

 

변수에 void 타입 지정하기

  • void 타입 변수에는 undefined만 담을 수 있습니다.
let a: void;

a = undefined;
  • 단, tsconfig.json에서 strictNullChecks: false로 설정하면 null도 허용됩니다.
// strictNullChecks: false 일 경우
let a: void;

a = undefined;
a = null;

 

never 타입

  • never는 값이 존재할 수 없음 을 뜻합니다.
  • 절대로 반환되지 않거나 실행이 끝나지 않는 코드를 표현할 때 사용합니다.
function func3(): never {
  while (true) {} // 무한 루프 → 절대 끝나지 않음
}
  • 오류를 던져서 함수가 강제로 종료되는 경우도 never 타입이다.
function func4(): never {
  throw new Error("Something went wrong");
}
  • 이 함수도 값을 반환할 수 없으니 never로 지정합니다.

 

변수에 never 타입 지정하기

  • never는 그 어떤 값도 담을 수 없는 타입입니다.
  • any 타입의 값조차도 들어갈 수 없습니다.
let a: never;

a = 1;          // 오류
a = null;       // 오류
a = undefined;  // 오류

 


 

마무리

이 글에서는 TypeScript의 기본 타입을 모두 정리해 보았습니다.

  • number, string, boolean, null, undefined
  • 배열(Array), 튜플(Tuple)
  • 객체(Object Literal Type)
  • 타입 별칭(Type Alias), 인덱스 시그니처(Index Signature)
  • enum, any, unknown, void, never

다음 글에서는 Section 03, 타입스크립트의 타입 이해하기를 정리할 예정입니다!