TypeScript

✅ TypeScript(타입스크립트)란?
- 마이크로소프트에서 개발한 JavaScript의 상위 집합(Superset) 언어
- JavaScript에 정적타입 검사와 클래스 기반 객체 지향 프로그래밍 등의 기능을 추가하여 개발된 언어로, JavaScript가 발전하면서 생긴 단점을 보완하기 위해 등장했다.
✅ TypeScript의 등장 배경
- Js는 처음에는 브라우저에서만 동작하는 스크립팅 언어로 만들어졌으나, 시간이 점점 흐르고 JavaScript로 웹 애플리케이션의 상호작용이 증가하면서, 웹 애플리케이션이 필요로 하는 Js 코드의 양이 폭발적으로 늘어났다.
- 이로 인해 Js의 한계가 부각되었고 동적 타입이 결정되어 유연하고, 다양한 라이브러리와 프레임워크를 사용할 수 있는 장점이 있지만, 타입의 명시성이 부족하다는 단점이 있다.
- 타입의 명시성이 부족하게 되면 아래 코드처럼 예상치 못한 결과를 초래할 수 있다.
let add = (x, y) => {
return x + y;
}
add(5, "7");
- 이런 문제를 보완하기 위해 TypeScript라는 언어가 등장하게 되었다.
✅ TypeScript 사용 시 장점
- 정적타입 검사 기능을 제공하며, 코드의 가독성과 유지 보수성을 높여준다.
: 이를 통해 개발자는 런타임 에러를 최소화하고, 코드 작성 시간을 단축하며, 협업 시 코드의 가독성을 높일 수 있다. - TS는 ES6 문법을 포함한 최신 JS 문법을 지원하며, 인터페이스, 제네릭, 데코레이터 등의 기능을 제공하여 객체지향 프로그래밍을 보다 쉽게 할 수 있도록 도와준다.
아래는 인터페이스를 사용하여 코드의 가독성을 높인 예시이다.
interface User {
id: number;
name: string;
}
function greetingUser(user: User) {
console.log(`Hello, ${user.name}!`)
}
const parkUser = {
id: 1,
name: "박해커"
};
greetingUser(parkUser);
TypeScript 데이터 타입
- Boolean 타입
- Number 타입
- String 타입
- Array 타입
- 선언 방법 2가지 - Tuple 타입 : 요소의 타입과 개수가 고정된 배열
- Object 타입
- Any 타입 : 어떤 타입이 들어올지 모르는 경우 사용 가능하나 필요한 경우가 아니면 지양할 것.
// Array 타입 선언 방식 2가지
// 첫 번째 방법
let items: string[] = ["apple", "banana", "grape"];
// 두 번째 방법
let numberList: Array<number> = [4, 7, 100];
// Tuple 타입
let user: [string, number, boolean] = ["kimcoding", 20, true];
// Any 타입
let maybe: any = 4;
maybe = true;
TypeScript에서 함수 사용하기
✅ TypeScript에서 함수 타입 명시하기
- JS에서 작성한 함수를 TypeScript로 변환하면, 매개변수와 반환 타입을 명시해야 한다.
// Named function
function add(x: number, y: number): number {
return x + y;
}
// Arrow function
let add = (x: number, y: number): number => {
return x + y;
};
- TypeScript에서는 함수의 매개변수 타입과 반환 타입을 명시해야 한다.
- 매개변수의 타입은 ':' 뒤에 작성
- 반환 타입은 괄호 '()' 뒤에 작성한다.(화살표 앞)
✅ 반환값이 없는 경우 (void)
- 반환값이 없는 경우 void 타입을 명시해야 한다.
let printAnswer = (): void => {
console.log("YES");
};
✅ 함수의 매개변수 검증
- TS는 JS와 달리 매개변수 개수에 맞춰 전달인자를 전달해야 한다.
let greeting = (firstName: string, lastName: string): string => {
return `hello, ${firstName} ${lastName}`;
};
// 에러 발생 (매개변수가 부족함)
greeting("coding");
// 정상적으로 작동
greeting("coding", "kim");
// 에러 발생 (매개변수가 너무 많음)
greeting("coding", "kim", "hacker");
✅ 기본 매개변수 (Default Parameter)
- 매개변수의 기본값을 설정하면, 값을 생략했을 때 해당 기본값이 사용된다.
let greeting = (firstName: string, lastName: string = "kim"): string => {
return `hello, ${firstName} ${lastName}`;
};
// 정상적으로 작동 (기본값 사용)
greeting("coding"); // "hello, coding kim"
// 정상적으로 작동
greeting("coding", undefined); // "hello, coding kim"
// 에러 발생 (매개변수가 너무 많음)
greeting("coding", "kim", "hacker");
✅ 선택적 매개변수 (Optional Parameter)
- 선택적 매개변수를 사용하려면 '?' 를 붙이면 된다.
let greeting = (firstName: string, lastName?: string): string => {
return `hello, ${firstName} ${lastName}`;
};
// 정상적으로 작동
greeting("coding"); // "hello, coding undefined"
greeting("coding", "kim"); // "hello, coding kim"
// 에러 발생 (매개변수가 너무 많음)
greeting("coding", "kim", "hacker");
이때 greeting("coding") 처럼 전달인자를 하나만 전달하면, 선택적 매개변수는 undefined가 된다.
TypeScript의 연산자 활용 타입
- JavaScript에서도 보았던 OR 연산자나 AND와 같은 연산자를 이용하여 만든다. | 연산자를 이용한 타입을 유니온(Union) 타입이라고 하며, & 연산자를 이용한 타입은 인터섹션(Intersection) 타입이라고 부른다.
✅ Union 타입
- 둘 이상의 타입을 합쳐서 만들어진 새로운 타입으로 | 연산자를 이용하며, OR의 의미이다.
- 다양한 타입의 값을 처리해야 하는 경우 유용하다.
function printValue(value: number|string): void {
if (typeof value === "number") {
console.log(`The value is a number: ${value}`);
} else {
console.log(`The value is a string: ${value}`);
}
}
printValue(10); // The value is a number: 10
printValue("hello"); // The value is a string: hello
장점
- 타입을 추론할 수 있기 때문에 타입에 관련된 API를 쉽게 자동완성으로 얻어낼 수 있다.
- 코드의 가독성을 높일 수 있다.
유의할 점
- 유니온 타입인 값이 있으면, 유니온에 있는 모든 타입에 공통인 멤버들에만 접근할 수 있기 때문에 유의해야한다.
interface Developer {
name: string;
skill: string;
}
interface Person {
name: string;
age: number;
}
function askSomeone(someone: Developer | Person) {
console.log(someone.name);
}
: 위의 코드처럼 인터페이스를 사용하여 Developer와 Person을 정의했지만 askSomeone 함수 내부에서는 두 인터페이스가 가지고 있는 공통 프로퍼티인 name 에만 접근할 수 있다. (공통되고 보장된 프로퍼티만 제공해야 하기 때문에)
* 만약 나머지 프로퍼티에도 접근하고 싶다면 타입 가드를 사용해야 한다.
타입 가드(Type Guard)란? TypeScript에서 타입을 보호하기 위해 사용되는 기능 중 하나로 특정 코드 블록에서 타입의 범위를 제한해 해당 코드 블록 안에서 타입 안정성을 보장해준다.
function askSomeone(someone: Developer | Person) {
// in 연산자 : 타입스크립트에서 객체의 속성이 존재하는지를 체크하는 연산자
// in 연산자는 객체의 속성 이름과 함께 사용하여 해당 속성이 객체 내에 존재하는지 여부를 검사
if ('skill' in someone) {
console.log(someone.skill);
}
if ('age' in someone) {
console.log(someone.age);
}
}
: TypeScript에서는 in 연산자를 제공한다. in 연산자는 객체의 프로퍼티 이름과 함께 사용되며, 해당 프로퍼티가 객체 내에 존재하는지 여부를 검사한다.
✅ 인터섹션(Intersection) 타입
- 인터섹션은 둘 이상의 타입을 결합하여 새로운 타입을 만드는 방법으로 & 연산자를 사용하여 표현한다.
interface Developer {
name: string;
skill: string;
}
interface Person {
name: string;
age: number;
}
function askSomeone(someone: Developer & Person) {
console.log(someone.age);
console.log(someone.name);
console.log(someone.skill);
}
: 위 코드는 인터섹션 타입을 사용하여 Developer와 Person을 하나의 타입으로 묶었고, 따라서 askSomeone 함수 내에선 정의된 프로퍼티에 전부 접근할 수 있다.
▶️ 그러나 인터섹션 타입은 타입 가드는 필요 없는 반면 Developer와 Person이라는 새로운 교집합을 만들어 내는 것이기 때문에, 전달인자를 전달할 때 모든 프로퍼티를 전부 보내줘야한다. 반대로 유니온 타입은 타입 가드를 해줘야 하지만 전달인자를 전달할 때 선택지가 생긴다.
interface Developer {
name: string;
skill: string;
}
interface Person {
name: string;
age: number;
}
function askSomeone(someone: Developer | Person) {
//이런 식으로 프로퍼티에 접근할 수 있습니다.
if ('skill' in someone) {
console.log(someone.skill);
}
if ('age' in someone) {
console.log(someone.age);
}
}
//유니온 타입은 전달인자를 전달할 때 선택지가 생깁니다.
askSomeone({name: '김코딩', skill: '웹 개발'});
askSomeone({name: '김코딩', age: 20});
function askSomeone2(someone: Developer & Person) {
//타입 가드를 사용하지 않아도 모든 프로퍼티에 접근할 수 있습니다.
console.log(someone.age);
console.log(someone.name);
console.log(someone.skill);
}
//그러나 인터섹션 타입으로 결합하게 된다면 전달인자를 전달할 때 선택지가 없습니다.
askSomeone2({name: '김코딩', skill: '웹 개발', age:20});
TypeScript의 Enum
- TypeScript의 Enum은 특정 값의 집합을 정의할 때 사용된다. JavaScript에서는 기본적으로 열거형을 지원하지 않지만, TypeScript에서는 문자형 열거형과 숫자형 열거형을 지원한다.
TypeScript에서 열거형은 기본적으로 아래와 같은 형태로 정의할 수 있다.
enum Color {
Red,
Green,
Blue,
}
✅ 숫자형 열거형(Enum)
- 열거형은 숫자형과 문자열형, 혹은 이 둘의 조합으로 정의될 수 있고 디폴트 값으로 숫자형을 사용하며, 각 값은 자동으로 0부터 시작하여 1씩 증가하지만 아래와 같이 수동으로 값을 지정할 수도 있다.
enum Color {
Red = 1,
Green = 2,
Blue = 4,
}
let c: Color = Color.Green;
let greenValue: number = Color.Green;
let blueValue: number = Color.Blue;
console.log(c); // 출력: 2
console.log(greenValue); // 출력: 2
console.log(blueValue); // 출력: 4
✅ 문자형 열거형(Enum)
- 문자형 열거형은 열거형의 값을 전부 다 특정 문자 또는 다른 열거형 값으로 초기화해야한다.
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
let myDirection: Direction = Direction.Up;
console.log(myDirection); // 출력: "UP"
✅ 역 매핑 (Reverse mappings)
- 역 매핑은 숫자형 열거형에만 존재하는 특징이다. 열거형의 key로 value를 얻을 수 있고 value로 key를 얻을 수도 있다.
enum Enum {
A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
** 이는 숫자형 열거형에만 존재한다.
TypeScript 에서의 인터페이스
- TypeScript에서 인터페이스는 일반적으로 타입 체크를 위해 사용이 된다. 인터페이스는 변수, 함수, 클래스에 사용할 수 있으며, 인터페이스에 선언된 프로퍼티 또는 메서드의 구현을 강제하여 일관성을 유지하도록 한다.
✅ 변수와 인터페이스
interface User {
name: string;
age: number;
}
// 정상적으로 선언됩니다.
const user: User = {
name: "anna",
age: 20
}
// 프로퍼티의 순서를 지키지 않아도 정상적으로 선언됩니다.
const user: User = {
age: 20,
name: "anna"
}
// 정의된 프로퍼티보다 적게 작성했기 때문에 에러가 납니다.
const user: User = {
name: "anna"
}
// 정의된 프로퍼티보다 많이 작성했기 때문에 에러가 납니다.
const user: User = {
name: "anna",
age: 20,
job: "developer"
}
인터페이스 내의 프로퍼티를 선택적으로 사용하려면 ? 연산자를 사용할 수 있다.
interface User {
name: string;
age?: number;
}
// 정상적으로 선언됩니다.
const user: User = {
name: "anna"
}
✅ 함수와 인터페이스
interface User {
name: string;
age: number;
job: string;
}
interface Greeting {
(user: User, greeting: string): string;
}
const greet: Greeting = (user, greeting) => {
return `${greeting}, ${user.name}! Your job : ${user.job}.`;
}
const user: User = {
name: "anna",
age: 30,
job: "developer"
};
const message = greet(user, "Hi");
console.log(message);
✅ 클래스와 인터페이스
interface Calculator {
add(x: number, y: number): number;
substract(x: number, y: number): number;
}
class SimpleCalculator implements Calculator {
add(x: number, y:number) {
return x + y;
}
substract(x: number, y: number) {
return x - y;
}
}
const caculator = new SimpleCalculator();
console.log(caculator.add(4, 9)); //13
console.log(caculator.substract(10, 5)); //5