eatthefrog

타입스크립트 기초 본문

프론트엔드 노트

타입스크립트 기초

eater_forg 2025. 7. 8. 17:49

자바스크립트와 타입 스크립트 (TS((JS))

타입스크립트를 자바스크립파일로 변환하는 컴파일러가 필요하다.

 

Benefits

  • Static typing(Type Checking)
    • Staticaly-typed: C++, C#, Java
    • dynamically-typed: JS, 파이썬, 루비
  • Code completion
  • Refactorin
  • Shorthand notations

 


자바스크립트의 동적 타이핑 문제로 재할당에 의해 변수의 타입은 언제든지 동적으로 변할 수있는건 const로 해결할 수 있는 문제다. 근데 왜 굳이 TypeScript를 사용할까?

 

 

 

 

const의 한계와 TypeScrit의 이점


1. 객체와 배열의 내부 변경

// ☑️ const
const user = { name: "김철수", age: 25 };
user.age = "스물다섯"; // 타입이 number에서 string으로 변경됨
user.newProperty = true; // 새로운 속성 추가

const numbers = [1, 2, 3];
numbers.push("4"); // string이 number 배열에 추가됨


// ✅ TypeScript: 타입 체크로 런타임 에러 방지
interface User {
  name: string;
  age: number;
}

const user: User = { name: "김철수", age: 25 };
user.age = "스물다섯"; // 컴파일 에러 발생

2. 함수 매개변수와 반환값

// ☑️ const
const calculateTotal = (items) => {
  // items가 배열인지, 객체인지, 숫자인지 알 수 없음
  return items.reduce((sum, item) => sum + item.price, 0);
};

// 런타임 에러 발생 가능
calculateTotal("잘못된 데이터");


// ✅ TypeScript: 함수 시그니처 명확화
const calculateTotal = (items: Array<{price: number}>): number => {
  return items.reduce((sum, item) => sum + item.price, 0);
};

3. API 응답 데이터

// ☑️ const
const fetchUserData = async () => {
  const response = await fetch('/api/user');
  const data = await response.json();
  // data의 구조를 알 수 없음
  return data.user.profile.name; // 존재하지 않는 속성에 접근할 수 있음
};

타입표기와 기본 자료형

타입스크립트의 타입 체킹 예제

let score: number = 90;

let studentName: stirng;
studentName = 'Jogn';

score = 95;        //✅ 타입스크립트도 let 변수 재할당 가능!
score = "95";      //❌ 타입스크립트의 타입 체킹

 

타입 스크립트에서 변수를 할당하는 3가지 방법

// 1. 명시적 타입 선언 + 초기화
// 타입: number로 명시적 지정, 초기값 90
let score: number = 90;
score1 = 95;        // ✅ 가능
score1 = "A+";      // ❌ 에러: string을 number에 할당 불가

// 2. 타입 추론 + 초기화
// 타입: number로 자동 추론, 초기값 90
let score = 90;
score2 = 95;        // ✅ 가능
score2 = "A+";      // ❌ 에러: string을 number에 할당 불가

// 3. 타입 미지정 + 초기값 없음
// 타입: any로 추론, 초기값: undefined
let score;
score3 = 90;        // ✅ 가능
score3 = "A+";      // ✅ 가능 (any 타입이라서)
score3 = true;      // ✅ 가능 (any 타입이라서)

 

any 타입

: 변수를 선언만하고 할당은 하지 않으면 'any' 타입으로 지정되고, 어떤 자료형이든 할당될 수 있다, 이는 타입스크립트를 사용하는 의미가 없게 하므로 지양해야할 코드다.

 

하지만 타입 정의가 없는 외부 라이브러리를 사용하거나 API로부터 데이터를 받아올때 다루어야할 데이터의 타입을 미리 알 수 없는 등의 상황에서 타입스크립트의 안전장치를 잠시 꺼두는 용도로 'any'가 사용된다.

//When 'any' is useful -working with third-party libraries

// data라는 매게변수를 받아서 반환하는 함수이고 이 data로 어떤 타입의 값이 주어질지는 예측할 수 없다.
// 의도된 타입의 값이 전달되었을 경우 'someProperty'속성에 접근해서 'someMethod'를 호출한 결과를 반환할 것이다.
// 만약 다른 타입이더라도 옵셔널 체이닝을 사용하여 오류를 방지한다. 그리고 OR 연산자를 사용해 의도된 타입이 아니라면 해당 데이터 자체 (data)를 반환한다.
function processData(data: any) {
	return data.someProperty?.someMethod?.() ||data;
}

 

 

unknown 타입

'any'가 타입을 모르는 데이터에 대해 관대해지는 반면 'unknown'은 오히려 엄격해진다. 

'any'는 타입을 모르니까 "뭐든 들어와"라면 unknown은 타입을 모르니까 "뭐든 들어오지마"로 일단 모든걸 금지한다.

let anyVar: any: 10;
let unknownVar: unKnown = 10;

let anyNumber: numner = anyVar;       //✅
anyVar.toFixed(2);                    //✅

let unknownVar: number = unknownVar;    //❌
unknownVar,toFixed(2);                  //❌

 

그래서 unKnown타입을 사용하려면 타입 가드라는 안전장치가 필요하다. 쉽게 말해, '만약 이 값의 타입이 무엇무엇이라면'과 같은 조건들을 두고 코딩하는 것이다. 

 

unknown 타입과 타입가드 예시

// unKnown 타입의 값을 처리하는 함수 예시
// 아래 처럼 해당 값의 실제 타입에 따른 코드를 각각 작성해준다. 

// function 함수명(매개변수: 매개변수타입): 반환타입 { 함수본문 }
// val: unknown → 매개변수 val은 unknown 타입
// : string → 함수는 반드시 string 타입을 반환해야 함 "이 함수는 어떤 입력을 받든 항상 문자열을 반환한다"

function processValue(val: unknown): string {
    if (typeof val === 'string') {
        return val.toUpperCase();    //✅ string 반환
    }
    
    if (typeof val === 'number') {
        return val.toFixed(2);       //✅ string 반환 (toFixed는 string 리턴)
    }
    
    return String(val);              // string 반환
}

console.log(processValue('hello'));  //✅ "HELLO"
console.log(processValue(42));       //✅ "42.00"
console.log(processValue(true));     //✅ "true"

 

unknown과 타입단언 as 예시

// 문자열 값이 들어있지만 타입은 'unknown'으로 지정된 변수 unknownValue
let unknownValue: unknown = "Hello, TypeScript";

// Type assertion - when you're certainof the type
// 괄호안에 'as'연산자를 사용하해서 해당 변수의 값이 문자열 타입임을 나타낸다.
// 이렇게 작성하면 타입스크립트는 이를 신뢰하고 컴파일 단계에서 오류를 발생시키지 않는다.
let stringLegnth = (unknwonValue as stirng).length;

// Type guard -safer
if(typeof unknownValue === 'stirng') {
	let length = unknwonvalue.length;
}

 

unknown과 any를 적절히 활용하여 외부 데이터를 안전하기 처리하기 예제

function processUserData(user: unknown): string {
    // 1단계: 객체인지 확인 (Type Guard)
    if (typeof user === 'object' && user !== null) {
        // 2단계: name 속성 존재 및 타입 확인
        if ('name' in user && typeof (user as any).name === 'string') {
            return (user as any).name.toUpperCase();
        }
    }
    return 'Invalid user data';
}

console.log(processUserData({ name: 123 }));        // "Invalid user data"
console.log(processUserData("John Doe"));           // "Invalid user data"

 unknown - 안전한 시작점

    - unknown: 어떤 타입인지 모르니까 일단 안전하게 시작
    - 바로 user.name 접근 불가 → 타입 체크 필요

any - 제한적이고 안전한 사용

- 'name' in user로 속성 존재는 확인했지만

- TypeScript는 여전히 user.name의 타입을 모름

- 따라서 (user as any).name으로 접근

 


 

유니온 타입

: 여러 타입 중 하나가 될 수 있는 타입을 나타내며, | 기호로 사용한다.

 

// 문자열 또는 숫자
let value: string | number;

value = "hello";    // ✅ 가능
value = 42;         // ✅ 가능
value = true;       // ❌ 에러

 

타입 스크립트 배열 선언 방법 2가지

1. 타입 [] 문법

let numbers: number[] = [1, 2, 3, 4, 5];
let scores: number[] = [];

2. Array<타입> 문법(제네릭 문법)

let fruits: Array<string> = ['Apple', 'Banana', 'Orange'];

타입 안전성 확인

let scores: number[] = [];
scores.push(95);      // ✅ 가능 (number)
scores.push(88);      // ✅ 가능 (number)
scores.push("A+");    // ❌ 에러 (string은 불가)

let colors = ['Red', 'Green', 'Blue'];  // string[]로 타입 추론
colors.push(123);     // ❌ 에러 (number는 불가)

타입 추론 VS 명시적 선언

// 타입 추론 (권장)
let colors = ['Red', 'Green', 'Blue'];        // string[]로 자동 추론

// 명시적 선언
let colors: string[] = ['Red', 'Green', 'Blue'];

// 빈 배열은 명시적 선언 필요
let scores: number[] = [];  // 타입 지정 필요

다양한 배열 타입 예제

// 유니언 타입 배열
let mixed: (string | number)[] = [1, "hello", 2, "world"];

// 대괄호([])를 붙여서, 객체 배열을 저장할 수도 있다. 
let users: { 
	name: string; 
    age: number 
}[] = [
    { name: "김철수", age: 25 },
    { name: "이영희", age: 30 }
];

let people: {
	name: string;
    age: number;
}[]'

// 읽기 전용 배열
let readonlyNumbers: readonly number[] = [1, 2, 3];
// readonlyNumbers.push(4); // ❌ 에러 (수정 불가)

타입 스크립트 배열 고차 함수의 타입추론과 반환 타입 코드 예제

  • 타입 스크립트는 배열 고차 함수의 입력/출력 타입을 자동 추론
  • 각 함수별로 적절한 반환 타입 제공 (map: 변환, filter: 같은 타입, find: 타입 | undefined)
  • 컴파일 타입에 대한 타입 안전성 보장

1. mpa() 배열 반환

// 타입 변환) map은 각 요소를 다른 타입으로 변환 가능
// stirng[] -> number[]

const names: string[] = ['Alice', 'Bob', 'Charlie', 'David'];
const nameLengths: number[] = names.map(name => name.length);
// 결과: [5, 3, 7, 5]

2. filter() 배열 필터링

 // 타입 유지) 조건에 맞는 요소만 선택
 // string[] -> string[](같은 타입 유지)
 
 const longNames: string[] = names.filter(name => name.length > 4);
// 결과: ['Alice', 'Charlie', 'David']

3. find() 단일 요소 찾기

// 반환타입) 첫 번재로 조건에 맞는 요소 반환
// string | undefined (찾지 못할 수도 있음)

const foundName: string | undefined = names.find(name => name.startsWith('B'));
// 결과: 'Bob' 또는 undefined

타입 추론의 장점

// 타입스크립트가 자동으로 추론
const names = ['Alice', 'Bob', 'Charlie'];  // string[]

// map 결과 자동 추론
const lengths = names.map(name => {
    // name은 자동으로 string 타입
    return name.length;  // 반환값은 number
});  // lengths는 number[]

// filter 결과 자동 추론
const shortNames = names.filter(name => {
    // name은 string 타입
    return name.length < 5;  // 조건은 boolean
});  // shortNames는 string[]

다양한 고차 함수 예제

const numbers = [1, 2, 3, 4, 5];

// reduce - 누적 계산
const sum: number = numbers.reduce((acc, num) => acc + num, 0);

// some - 조건 만족 여부
const hasEven: boolean = numbers.some(num => num % 2 === 0);

// every - 모든 요소 조건 만족 여부
const allPositive: boolean = numbers.every(num => num > 0);

// forEach - 각 요소 순회 (반환값 없음)
numbers.forEach(num => console.log(num));  // void

타입 안전성 보장

// 타입스크립트의 단계별 타입 체크
const names: string[] = ['Alice', 'Bob'];

names.map(name => {
    // 1. name의 타입 확인: string
    // 2. name에 사용 가능한 메서드 확인
    // 3. 반환값 타입 확인
    
    return name.toUpperCase();  // ✅ string → string (정상)
    return name.toFixed(2);     // ❌ string에 toFixed 없음
});

names.filter(name => {
    // 1. name의 타입 확인: string
    // 2. 반환값이 boolean인지 확인
    
    return name.length > 3;     // ✅ boolean (정상)
    return name.length;         // ❌ number (boolean 아님)
});

타입스크립트 튜플

  • 튜플: 서로 다른 타입, 고정 길이, 순서 중요
  • 배열: 같은 타입, 가변 길이, 순서 상관없음
  • 활용 예시: 함수의 다중 반환값, 좌표

기본 튜플 사용법

// 첫 번째 요소는 반드시 stirng
// 두 번재 요소는 반드시 number
// 정확히 2개 요소만 허용

let person: [string, number] = ['John', 30];

console.log(person[0]); // 'John' (string)
console.log(person[1]); // 30 (number)

타입 안전성

// ❌ 에러: 순서가 맞지 않음
person = [30, 'John'];

// ❌ 에러: 길이가 맞지 않음 (3개 요소)
person = ['John', 30, true];

구조 분해 할당

// 각 변수가 올바른 타입으로 자동 추론됨

const [firstName, age] = person;
console.log(firstName); // 'John' (string 타입)
console.log(age);       // 30 (number 타입)

선택적 요소 (Oprional)

// boolean?: 세 번째 요소는 있어도 되고 없어도 됨

type OptionalTuple = [string, number, boolean?];

const complete: OptionalTuple = ['Jane', 25, true];  // ✅ 3개 요소
const partial: OptionalTuple = ['Mike', 40];         // ✅ 2개 요소 (3번째 생략)

일반 배열과의 차이점

// 튜플 - 타입과 순서 고정
let tuple: [string, number] = ['John', 30];
tuple[0] = 'Jane';  // ✅ string만 가능
tuple[1] = 25;      // ✅ number만 가능

// 배열 - 모든 요소가 같은 타입
let array: string[] = ['John', 'Jane', 'Mike'];
array.push('Tom');  // ✅ 길이 제한 없음

타입스크립트 객체 타입 선언과 에러 검증 예시

타입 스크립트는 객체 생성 시점에 모든 속성이 올바른 타입인지, 필수 속성이 누락되지 않아는지 검증해서 런타임 에러를 미리 방지한다.

  • 객체의 구조와 타입을 미리 정의
  • 컴파일 타임에 타입 불일치와 누락된 속성 검출
  • 코드 작성 시 자동완성과 에러 방지 제공

객체 타입 선언 방법

// 인라인 타입 선언
//name:string -  name 속성은 문자열이어야함
//age:number - age 속성은 숫자여야 함

const person: { name: string; age: number } = {
    name: "John",
    age: 25,
};

타입 안전성 검증

// 1. 타입 불일치 에러
const wrongPerson1: { name: string; age: number } = {
    name: "John",
    age: "25",  // ❌ 에러: string을 number에 할당 불가
};
// 2. 필수 속성 누락 에러
const wrongPerson2: { name: string; age: number } = {
    name: "Mark",
    // ❌ 에러: age 속성이 누락됨
};

올바른 사용 예시

// ✅ 정상: 모든 타입이 일치
const person: { name: string; age: number } = {
    name: "John",
    age: 25,
};

// ✅ 정상: 타입 추론 활용
const person2 = {
    name: "Jane",  // string으로 추론
    age: 30,       // number로 추론
};

선택적 속성

// ? 를 사용한 선택적 속성
const person3: { name: string; age?: number } = {
    name: "Mike",
    // age는 있어도 되고 없어도 됨
};

const person4: { name: string; age?: number } = {
    name: "Sara",
    age: 28,  // 있어도 됨
};

인터페이스로 개선

// 재사용 가능한 타입 정의
interface Person {
    name: string;
    age: number;
}

const person: Person = {
    name: "John",
    age: 25,
};