[웹 풀 사이클 데브코스 TIL - 11] Day2 - Typescript란?

이다미·2024년 5월 8일

📂 Typescript 란?

타입스크립트 = 자바스크립트 + 타입체크

타입스크립트는 자바스크립트와 달리 브라우저에서 실행하려면 파일을 한번 변환해주어야 한다. 이 변환 과정을 우리는 컴파일(complile) 이라고 부른다.

타입 체크가 필요한 이유

자바스크립트의 코드가 너무 지저분하거나, 코드의 스케일이 커지면서 코드 관리가 되지 않는 경우도 있다.
이 때 타입을 부여하는 타입스크립트를 사용한다면?

  • 자바스크립트 기반 코드보다 버그를 줄일 수 있다.
  • 유지보수가 쉽다.
  • 강력한 높은 퀄리티의 코드를 생산할 수 있다.

→ 데이터 타입을 표기함으로써 컴파일 타임 시 오류 체크가 쉽게 가능하다.

📂 데이터 타입과 추론

자바스크립트의 변수에는 어떤 데이터 타입의 값도 할당될 수 있다.
ex) 숫자, 문자열, 배열, ...

예를 들어 대학교 학번을 대입하려고 할 때 숫자 타입이 아닌 문자열 타입으로 넣는다면?

let studentId = 202410000;	// number
studentId = "202410000";	// string

자바스크립트는 값을 재할당 할 때 값의 타입이 바뀌어도 문제로 삼지 않기 때문에 사전에 막지 못한다.

(조건문을 통해 타입을 체크하여 재할당하는 방법도 있겠지만, 타입스크립트를 사용하는 것이 훨씬 효율적이다.)

타입 추론

변수를 선언할 때 타입을 쓰지 않아도 컴파일이 자동으로 타입을 판단해주는 것

let age = 25;

예를 들어 위와 같이 타입을 생략한 채 변수를 선언한 구문이 있다면, 타입스크립트는 age 변수의 타입을 자동으로 number로 추론한다.

타입 추론은 코드를 간결하게 작성할 수 있도록 도와준다는 장점이 있지만, 그럼에도 불구하고 타입을 명시적으로 지정하는 것이 명확하기 때문에 꼭!!! 타입을 명시해야 한다.

📂 Typescript 타입 명시

변수를 선언할 때 변수 값의 타입을 명시함으로써, 변수의 데이터 타입을 지정한다.

문법 및 사용법은 아래와 같다.

let 변수명: 타입 = 타입에 맞는 데이터;

// 예시
let year: number = 2024;
let str: string = "Hello World!";

→ 변수 선언 시 데이터 타입을 지정한다.
(TS를 JS로 컴파일할 때 데이터 타입에 다른 값이 할당되면 오류를 발생시켜 개발자에게 알려준다.)

만약 같은 변수에 대해 값을 재할당 하려고 했을 때, 타입이 다른 값을 재할당하려는 경우 에러가 발생한다.

let name: string = "Lee";
name = 1;

→ name 변수에 string 타입을 지정했기 때문에, number 타입인 1을 할당하면 에러를 반환한다.

함수의 데이터 타입 명시 (매개변수, 리턴값)

함수의 매개변수에도 타입 지정을 해주고, 리턴값에도 타입 지정을 해주어야 한다.
다음 예시를 간단하게 분석해보자.

// 리턴값이 존재할 때 타입 지정 (number, string, ...)
function plus(a: number, b: number): number {
  return a + b;
}

// 리턴값이 없는 함수 (void)
function plus(a: number, b: number): void {
	...
}
  • 매개변수 a, b 타입을 number 로 명시하고, 리턴값에도 타입을 지정해주기 위해 함수 바로 뒤에 number 로 타입을 명시해준다.
  • 아무 값도 리턴하지 않을 때 void 타입을 사용한다. (void 타입일 때 값을 리턴값은 존재할 수 없음)

ex) 학생 정보를 담는 함수 (매개변수, 리턴값 타입 지정)

// 매개변수 : id
// 리턴값 : 학번, 이름, 나이, 성별, 졸업여부
function studentInfo(id: number): {
  studentId: number;
  name: string;
  age: number;
  gender: string;
  graduation: boolean;
} {
  return {
    studentId: id,
    name: "Lee",
    age: 20,
    gender: "female",
    graduation: false
  }
}

함수의 리턴값에 정보들이 담겨져와야 하기 때문에, 객체 형식으로 타입을 각각 지정해준다.
하지만 위처럼 구현했을 시 코드가 매우 복잡해 보이므로, 이 때 인터페이스(interface)를 사용한다.

📂 인터페이스

상호 간의 정의한 약속 혹은 규칙 (사용자 정의 타입)

즉, 인터페이스에 정의된 프로퍼티 혹은 메소드의 타입을 다른 곳에 적용시키는 것이다.
몇 가지 특징이 있다.

  • 인터페이스를 한 번 정의하면 언제든지 재사용이 가능하다.
  • 선택적 프로퍼티로 지정하려면 속성값 뒤에 “ ? ” 를 붙여주어야 한다.
  • 메소드도 인터페이스 내에서 선언 가능하다. (메소드 오버라이딩)
  • implements를 통해 인터페이스를 클래스에 상속할 수 있다. (기능을 확장시키는 expends 같은 것 X)

위에서 구현한 복잡한 리턴값 타입을 인터페이스로 구현해보자.

interface Student {
  studentId: number;
  name: string;
  age: number;
  gender: string;
  graduation: boolean;
}
// Student 인터페이스를 함수 리턴값 타입에 적용
function studentInfo(id: number): Student {
  return {
    studentId: id,
    name: "Lee",
    age: 20,
    gender: "female",
    graduation: false
  }
}

console.log(studentInfo(202410000));

코드가 정상적으로 작동하는지 확인하기 위해 콘솔문을 추가했다.
결과값으로 내가 쓴 studentId와 나머지 정보들이 잘 출력되는 것을 확인할 수 있다.

만약 Student 인터페이스 타입을 사용할 때 하나의 정보라도 빠지면 어떻게 될까?
예시로 gender 항목을 주석처리해보자.

바로 에러가 발생한다^^
에러문은 "gender 항목이 선언되어야 해요" 라는 내용이다.

Student 인터페이스가 리턴값의 타입으로 지정되었기 때문에 리턴값에 해당하는 모든 항목이 존재해야 하는데, 하나의 항목이 빠지니까 인터페이스 완전체가 될 수 없고 결국 타입이 맞지 않는다는 말이다.

하지만 상황에 따라 몇 가지 항목만 사용해야 할 때도 있는데, 이럴 때에는 또 다시 그에 맞는 인터페이스를 만들어야 하는가?

답은 ❌
→ 상황에 맞춰서 사용할 수 있게끔, 있어도 되고 없어도 되는 프로퍼티가 돼야 한다.

선택적 프로퍼티 " ? "

선택적으로 사용할 프로퍼티 바로 뒤에 ? 를 붙여주면 된다.

이러한 방법은 함수의 매개변수에도 적용이 가능하다.

💡 주의할 점 : 선택적 매개변수를 설정하는 순간, 그 뒤에 있는 모든 매개변수는 선택적 매개변수가 돼야 한다.

→ 즉, 필수 매개변수는 무 조 건 선택적 매개변수 앞에 위치해야 한다.
(선택적 매개변수 예시는 다음 글에서,,,)

ex) 위 코드의 에러 해결하기 (gender 프로퍼티)

interface Student {
  studentId: number;
  name: string;
  age: number;
  gender?: string;
  graduation: boolean;
}
// Student 인터페이스를 함수 리턴값 타입에 적용
function studentInfo(id: number): Student {
  return {
    studentId: id,
    name: "Lee",
    age: 20,
    graduation: false
  }
}

console.log(studentInfo(202410000));

위와 같이 gender 를 선택적 프로퍼티로 지정해주면, studentInfo 함수의 리턴값에 gender 항목이 없어도 오류가 나지 않고 결과값이 정상적으로 반환된다.

class의 인터페이스 구현 방식

interface Student {
  studentId: number;
  name: string;
  age: number;
  gender?: string;
  graduation: boolean;
  setName: (name: string) => void;
}

class MyStudent implements Student {
  studentId = 202410000;
  name = "Lee";
  age = 20;
  gender = "female";
  graduation = false;
  setName(rename: string): void {
    this.name = rename;
    console.log(`이름 재설정 : ${this.name}`);
  };
}

const myStudent = new MyStudent();
myStudent.setName("Kim");

이름을 수정하는 코드를 구현하기 위해 Student 인터페이스에 setName함수 프로퍼티를 추가했다.
따라서 MyStudent 클래스에도 setName 함수 프로퍼티가 존재해야 하며, 매개변수로 받을 재설정 할 이름은 마지막줄 myStudent.setName("Kim"); 에서 결정한다.

📂 열거형 (enum)

마찬가지로 사용자 정의 타입이다.

// TS
enum GenderType {
  Male,
  Female,
  GenderNeutral
}

// JS
var GenderType;
(function (GenderType) {
    GenderType[GenderType["Male"] = 0] = "Male";
    GenderType[GenderType["Female"] = 1] = "Female";
    GenderType[GenderType["GenderNeutral"] = 2] = "GenderNeutral";
})(GenderType || (GenderType = {}));

만약 TS 에서 GenderType 을 정의할 때 Male = ‘male’ 이런식으로 정의한다면?

// TS
enum GenderType {
  Male = 'male',
  Female = 'female',
  GenderNeutral = 'neutral'
}

// JS
var GenderType;
(function (GenderType) {
    GenderType["Male"] = "male";
    GenderType["Female"] = "female";
    GenderType["GenderNeutral"] = "neutral";
})(GenderType || (GenderType = {}));

인덱스값(숫자) 대신 직접 부여한 값(문자열)이 들어가는 것을 확인할 수 있다.


출처
인터페이스 | 타입스크립트 핸드북
타입 추론 / 타입 호환 / 타입 단언 / 타입 가드 💯 총정리
Typescript - 인터페이스 vs 추상클래스

0개의 댓글