[TS] 타입스크립트의 다양한 타입

박하늘·2025년 4월 8일

▪️ Arrays

  • 배열의 모든 요소가 “동일한” 타입을 갖도록 명시
  • 이를 통해 배열 내부 요소의 타입 안정성 제공
  • 두 가지 방법으로 표현
    1. type[ ]
    2. Array<type>

1️⃣ 예제

// 1. type[ ]
let nums: number[] = [1, 2, 3, 4, 5] // 숫자만 허용
let color: string[] = ["red", "green", "blue"] // 문자열만 허용

nums.push(7) // 가능 : 숫자 타입만 허용
nums.push("8") // 불가능 : 숫자 타입에 문자열 타입을 넣을 수 없다는 오류 발생
nums[0] = "1" // 불가능 : nums 의 0번째 인덱스에 문자 타입을 할당할 수 없다는 오류 발생
  
// 2. Array<type>
let str: Array<string> = ["foo", "bar", "baz"]
let flags: Array<boolean> = [false, false, false]

falags.push(1) // 불가능 : 불리언 타입만 허용 가능



▪️ Objects

  • 객체의 각 속성(property)과 해당 속성의 타입을 명시적으로 정의
  • “타입을 사용해 객체의 형태(shape) 를 표현한다”

[ 객체 타입을 통해 ]
• 객체의 형태/구조를 보장하고 예측
• 예상치 못한 값이 들어가는 것을 방지

[ 객체의 Property 타입 ]
• optional (옵셔널): 특정 속성이 필수가 아닌 선택적임을 표현, 속성이름 뒤에 물음표 (“?”) 를 사용
• readonly (읽기전용): 객체의 속성이 수정되지 않아야할때 사용. 속성 이름 앞에 “readonly” 키워드를 추가하여 사용

[ 타입 별칭 (Type Alias) ]
• type 키워드를 사용하여 타입 별칭을 생성한다
• 객체의 구조를 정의하고, 정의된 구조에 이름(별칭)을부여하는 방법


1️⃣ 기본구조

// 기본 객체 타입

// {
// property_name: property_type
// }

let car: { make: string; model: string, year: number }

car = {
  make: "Volkswagen", 
  model: "Tiguan",
  year: 2020
}

2️⃣ 예제

// 📍 [타입 명시]
let monitor: { brand: string; price: number }

// CASE: 1
// monitor.brand = "LG"
// monitor-price = 120

// CASE: 2
monitor = {
    brand: "LG", 
    price: 120
}

// 오류 : 모니터 객체에 지정한 타입 내에 존재하지 않음
monitor.displaySize = "22inch" 

// 오류 : price 프로퍼티를 설정하지 않음
monitor = {
    brand: "LG"
}




// 📍 [타입 추론]
// 객체를 생성하면서 임의적인 타입 구조가 완성됨 이로 타입을 추론함

// monitor1 위에 마우스 호버 시 brand 는 문자 타입으로, price 는 숫자 타입으로 자동 추론
let monitor1 = {
    brand: "LG", 
    price: 120
}

// 오류 : price 에는 숫자 타입만 할당 가능
monitor1.price = "123"

// 오류 : 모니터 객체에 지정한 타입 내에 존재하지 않음 오류 발생
monitor1.displaySize = "22inch" 




// 📍 [Optional 프로퍼티] - 선택사항

let user: { id: string; name: string; age?: number };

// age 값은 optional 프로퍼티 (?) 설정으로 선택사항임. 넣지 않아도 오류 발생하지 않음
user = {
    id: "1234",
    name: "John"
}




// 📍 [Readonly 프로퍼티]
let user1: { readonly id: string; name: string; age?: number };

user1 = {
    id: "1234",
    name: "John"
}

// 정상
user1.name = "John Smith";

// 오류 : Readonly 를 사용하여 처음 할당 된 1234 라는 값으로 고정됨
// Cannot assign to 'id' because it is a read-only property.
user1.id = "5678"

// + Readonly 프로퍼티 예제 중 자주 쓰이는 문법
let apiConfig: {
    readonly clientKey: string; 
    readonly url: string;
}




// 📍 [타입 별칭 (Type Alias)]
// 하나의 객체 타입을 만들어서 변수처럼 여러번 사용 가능

type UserType = {
    id: string; 
    name: string; 
    age: number;
}

let user11: UserType = {
    id: "1", 
    name: "John", 
    age: 20
    }

// 오류 가능성 : 처음 할당한 age의 값이 없거나, 아예 없는 값인 height의 값을 넣게 되면 오류 발생
let user22: UserType = {
    id: "2",
    name: "Sarah",
    age: 20,
    // height : 160
};

let users: UserType[] = [];

// 정상 : UserType 의 객체 형태만 원하기 때문에 가능
users.push(user11); 
users.push(user22);

// 오류 : UserType의 형태가 아님
users.push({}) ; 

// 오류 : UserType의 형태가 아니며, 모든 프로퍼티가 들어가지 않았음
users.push({
    id: "1"
});




// 📍 Nested 객체 ( 중첩 객체 )

type Payload = {
    timestamp: number; 
    type: string;
    user: {
        readonly id: string; 
        isActive?: boolean;
        email: string[];
    }
}

const payload: Payload = {
    timestamp: 12893718, 
    type: 'event',
    user: {
    id: "123",
    isActive: true,
    email: ["qwer12@gmail.com", "qwer112@gmail.com"]
    }
}



▪️ Functions

  • 함수의 파라미터(매개변수) 와 반환값에 타입 지정
  • 예상치 못한 타입의 인자가 전달 되거나 타입의 값이 반환되는 것을 방지
  • 함수 시그니처로 해당 함수의 사용법을 자연스럽게 문서화

1️⃣ 기본 문법

function add(x: number, y: number): number { 
  return x + y;
}

function sayHi(name: string, greeting: string = "Hi"): string {
	return `${greeting}, ${name}`;
}

2️⃣ 다양한 함수 타입

  • Parameter 타입 : 함수에 전달하는 매개 변수의 데이터타입을 지정하며, 올바른 개수가 전달되는지 또한 확인한다
  • Optional Parameter 타입 : 함수에 필수가 아닌 선택적 매개변수를 지정할 수 있다
  • Contextual 타입 : 함수(콜백 함수)의 맥락에 따라 자동으로 타입을 추론하는 기능
  • Return 타입 : 함수가 반환하는 값의 데이터 타입을 명시

3️⃣ void & never

  • void 반환 타입 : 아무것도 반환하지 않는 함수
  • never 반환 타입 : 절대 반환하지 않는 함수

4️⃣ 예시

// 📍 Parameter
function add(x: number, y: number): number {
    return x + y;
    }

// 반환 타입도 number 이므로 result 도 자동적으로 number 타입으로 명시됨
const result = add (10, 5);




// 📍 Parameter II
function addToCart(name: string, price: number, quantity: number) {
    return `${name}, ${price}, ${quantity}`;
}

// 정상
addToCart("orange", 100, 5)

// 오류 : Expected 3 arguments, but got 2.
addToCart("pineapple", 150)

// 오류 : Expected 3 arguments, but got 4.
addToCart ("pineapple", 150, 5, false)




// 📍 Defalt Parameter
// quantity = number 타입으로 1이라는 파라미터가 기본 설정 -> 생략 가능
function addToCart1(name: string, price: number, quantity: number = 1) {
    return `${name}, ${price}, ${quantity}`;
}

addToCart1("orange", 10) // "orange, 10, 1" 반환 
addToCart1("pineapple", 5, 3) // "pineapple, 5, 3" 반환




// 📍 optional Parameter
function addToCart2(name: string, price: number, quantity?: number ) {
    return `${name}, ${price}, ${quantity}`;
}

addToCart2("orange", 150) // "orange, 150" 반환 
addToCart2("pineapple", 100, 2) // "pineapple, 100, 2" 반환

// + 추가 optional Parameter
// 아래 코드로 작성 시 quantity의 경우 선택이지만, 따로 지정하지 않을 경우 1로 설정
function addToCart3(name: string, price: number, quantity?: number ) {
    return `${name}, ${price}, ${quantity || 1}`;
}




// 📍 Contextual Typing
// 타입을 명시하지 않았는데도, 타입스크립트가 주변 문맥을 보고 타입을 추론하는 것

const nums = [1, 2, 3];

const doubled = nums.map(num => num * 2);
// 여기서 num은 타입을 명시하지 않았지만 타입스크립트는 nums가 number[]임을 알고 있어서 num의 타입을 자동으로 number로 추론함




// 📍 Return Type annotation (반환 타입 명시)
// 함수가 예상치 못한 타입의 리턴값을 방지하기 위함
function addTwoValues(x: number, y: number): string {
    return `${x}${y}`;
}

function addTwoNumbers(x: number, y: number): number {
    return x + y;
}

function isTen(x: number, y: number): boolean {
return x + y === 10;
}




// 📍 Void: 해당 함수가 반환 값이 없다. /  never: 해당함수는 절대로 반환 값이 존재하지 않음을 명시

// console.log()처럼 뭔가 실행되긴 하지만 값을 반환하지 않는 함수에 사용
// 흔히 말하는 "부수 효과(Side Effect)" 함수들
function logMessage(message:string): void {
    console. log (message)
}

// 이 함수는 실행 도중 예외를 던지고 종료됨
// 따라서 "아무것도 반환할 수 없다" → never
function throwError (message: string): never {
    throw new Error(message)
}



▪️ Unions

  • 여러 타입 중 하나가 될 수 있는 값을 표현
  • 집합 이론의 “합집합” 과 동일한 개념 (A ∪ B)
  • 서로 다른 타입의 값을 하나의 변수에 할당할 수 있으며 “|” 를 사용하여 나타낸다

1️⃣ 기본 문법

// userId 의 타입은 string 또는 number 가 될 수 있다.
let userId: string | number;

userId = 1
userId = "1"

// id 파라미터의 타입은 string 또는 number 가 될 수 있다
function printUserId(id: string | number) {
	console. log(userId)
}

// 모두 가능
printUserId("1")
printUserId(1)

2️⃣ 예제

// 📍 type narrowing
// 유니언 타입의 구체적인 하위타입을 좁혀나가는 방식 (“타입 좁히기” 라고도 표현한다)
// 변수의 타입이 여러 가지일 수 있을 때, 조건문 등을 통해 구체적인 타입으로 좁히는 것

function processValue(value: number | string) {
    // 문자열 경우
    if(typeof value === 'string') {
        return value.toUpperCase; // string으로 좁혀짐
    }
    
    // 숫자의 경우
    return value.toString().toUpperCase(); // number로 좁혀짐
}




// 📍 배열 사용 예시
// 서로 다른 타입의 요소를 포함할 수 있는 배열을 정의하여 유연성을 증가시킨다
let mixedValues: (string | number)[] = [];

mixedValues.push("100")
mixedValues.push(100)

// 오류 : []는 빈 배열, 즉 타입스크립트 입장에서 보면 never[] 또는 any[] 같은 “배열 타입”
mixedValues.push([])




// 📍 리터럴과 유니언 타입
// 제한된 값을 가진 타입(예: 특정 문자열이나 숫자의 집합) 을 표현

const toggle = (option: "on" | "off") => {
    console. log(option);
}
toggle ("on")
toggle ("off")

// 오류 : "on" | "off" 외에 불가
toggle (true)
toggle (0)

type Size = "xs" | "s" | "m" | "1" | "xl";

let tShirtSize: Size;

tShirtSize = "m"

// 오류 : "xs" | "s" | "m" | "1" | "xl" 여기서 선언해준 값만 사용 가능하다
tShirtSize = "xxl"


// 아래 코드처럼 사용 가능
function setSize(size: Size) {
    switch(size) {
        case "xs" :
            // Xs 처리
            break;
        case "s" :
            // s 처리
            break;
        //..
    }
}





// 📍 type aliases와 유니언
// 타입 또는 리터럴값을 결합하여 재사용 가능한 타입을 정의

type SuccessCode = 200 | 201 | 202
type ErrorCode = 500 | 501 | 503

let responseCode: SuccessCode | ErrorCode

responseCode = 200
responseCode = 503

// 오류 : Type '403' is not assignable to type 'SuccessCode | ErrorCode'.
responseCode = 403



▪️ Interfaces

  • 객체의 구조/형태를 정의, 특정 객체가 어떤 Property, Method 를 가지고 있어야 하는지를 나타낸다
  • 클래스나 객체가 지켜야할 “계약” 같은 역할
  • 어떤 구조를 가져야 하는지
  • 확장 및 조합이 용이하다

1️⃣ 기본 문법

interface Person {
  name: string;
  age: number;
}

interface Student extends Person {
  studentNumber: number;
}

// 확장 사용
const student: Student = {
  name: "Amy", 
  age: 26, 
  studentNumber: 12345,
}

2️⃣ type alias vs interface

두 기능의 목적은 크게 다르지 않다. 크게 확장 문법이 다르다

type alias

  • type User = {}
  • 기존 타입에 이름을 붙이는 방법
  • 객체 포함, 리터럴, 원시값 등을 타입으로 가질 수 있다

interface

  • interface User {}
  • 객체 타입(형태)으로 사용해야 한다
  • 객체의 구조를 정의하는 데 사용되는 타입

🎀 예제로 비교

💡 [ 객체 타입 정의 ]

// type alias
type Person = {
  name: string;
  age: number;
};

// interface
interface Person {
  name: string;
  age: number;
}

💡 [ 확장 (상속) ]

// interface 확장
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

// type 확장 (교차 타입 사용)
type Animal2 = {
  name: string;
};

type Dog2 = Animal2 & {
  breed: string;
};

💡 [ 유니언 타입 (type만 가능) ]

type Status = "loading" | "success" | "error"; // ✅ type만 가능

// interface Status = "loading" | "success" ❌ 불가능

💡 [ 함수 타입 ]

// type
type Greet = (name: string) => string;

// interface
interface GreetFn {
  (name: string): string;
}

3️⃣ 예제

// 📍 readonly & optional property
interface User {
    readonly id: number; 
    name: string;
    isPremium?: boolean;
}
const testUser: User = {
    id: 100, 
    name: "John"
    }

// 오류 : readonly 이기 때문에 할당 불가
testUser. id = 200




// 📍 메서드/함수 타입
interface User1 {
    readonly id: number; 
    name: string;
    isPremium?: boolean;
    greet(message: string): string;
}

const testUser1: User1 = {
    id: 100, 
    name: "Nathan",
    isPremium: false,
    greet(message: string) {
        return `${message}, ${this.name}`;
    },
}

testUser1.greet("Hello"); // Hello, Nathan




// 📍 확장 및 조합
// 두 개 이상의 인터페이스 조합도 가능하다

interface Person {
    name: string;
    age: number;
  }

interface Employee {
    EmployeeId : number;
  }

  
  interface Student extends Person, Employee {
    studentNumber: number;
  }
  
  // 확장 사용
  const student: Student = {
    name: "Amy", 
    age: 26, 
    studentNumber: 12345,
    EmployeeId: 45678
  }



▪️ Tuples

  • 튜플 (tuple) 이란, 고정된 크기의 배열
  • 각 요소가 다른 타입을 가질 수 있고, 순서가 중요
  • 자바스크립트에는 존재 하지 않는 개념

1️⃣ 기본 문법

let myTuple: [string, number, boolean];

myTuple = ["Hello", 42, true]; // OK

myTuple = ["Hello", false, 42]; // Error : number, boolean 순서 바뀜
myTuple = ["Hello", 42]; // Error : boolean 값 명시 X 

2️⃣ 예제

// 📍 [1]
function getUserInfo(): [number, string] {
    return [1, "Sam"]
}

// 자동으로 userid 는 number 타입, username은 string 타입 으로 지정된다
const [userid, username] = getUserInfo();




// 📍 [2]
type Flavor = string;
type Price = number;
type IceCream = [Flavor, Price]

const vanila: IceCream = ["Vanila", 500]

// 자동으로 0번째는 string , 1번째는 number 타입이다.
vanila[0]
vanila[1]




// 📍 [3]

type lat = number;
type lng = number;
type coord = [lat, lng];
let coords: coord[] = [];

coords. push([36, -95]); 
coords. push ([38, -76]);

coords.push([false, "1"]);

// 타입 안정성 보장
for (const [lat, lng] of coords) {
    console. log(lat, lng);
}



▪️ Enum

  • 상수의 명명된 집합을 쉽게 다루기 위해 사용
  • 여러 상수 값들을 그룹화 하는 것이 목적
  • 자바스크립트에는 존재 하지 않는 개념

1️⃣ 기본 문법

// enum 안에 있는 상수 값들을 enum의 멤버 라고 한다.
// 그리고 멤버는 각자마다 할당값이 존재한다

// 아예 명시하지 않으면 상수값1 = 0 부터 1씩 증가 하는 값으로 지정되며
// 상수값 1 = 1 으로만 지정했다면 다음은 2, 3, --
// 상수값 2= 15 로만 지정했다면 다음은 16, 17 --

 enum 이름 {
     상수값 1,
     상수값 2
   }

2️⃣ 예제

// 📍 [숫자형 Enum]
enum PlayerState {
    Buffering, // 0
    Playing,  // 1
    Paused, // 2
    Seeking, // 3
  }

// enum에 있는 값들 중 하나를 갖게 할 때 또는 비교할 때 사용

let currentState: PlayerState;

// currentState = PlayerState.Buffering;
currentState = PlayerState.Playing;

// 오류 : 문자열로 바로 넣는 건 불가능
currentState = "Playing"

const isPlaying = (state: PlayerState) => {
    return state === PlayerState.Playing;
}

// 플레이어가 재생중인지 확인
isPlaying (currentState) ; // true : PlayerState.Playing 값을 명시 함




// 📍 [문자형 Enum]
enum Direction {
    Left = "LEFT",
    Right = "RIGHT",
    Up = "UP",
    Down = "DOWN"
  }

function move(dir: Direction) {
    switch(dir) {
    case Direction.Left:
        // 왼쪽 로직
        break;
    case Direction. Right:
        // 오른쪽 로직
        break;
    // ...
    }
}

move (Direction. Left);
move(Direction.Right);

참고 자료

https://stackblitz.com/edit/vitejs-vite-9myckcey?file=index.ts

0개의 댓글