[TypeScript] 기초

비트·2023년 7월 26일
0

CodeStates

목록 보기
51/54
post-thumbnail

TypeScript(타입스크립트)란

  • TypeScript는 마이크로소프트에서 개발한 JavaScript의 상위 집합(Superset) 언어.
    • JavaScript에 정적타입 검사와 클래스 기반 객체 지향 프로그래밍 등의 기능을 추가하여 개발된 언어
    • JavaScript가 발전하면서 생긴 단점을 보완하기 위해 등장

TypeScript의 등장 배경

JavaScript는 처음에는 브라우저에서만 동작하는 스크립팅 언어로 만들어졌다.

let add = (x, y) => {
	return x + y;
}

add(5, "7");
// 숫자 5와 문자열 "7"을 전달인자로 전달 결과는 "57"이 나온다. 

JavaScript는 함수나 변수의 타입을 명시적으로 지정하지 않아도 동작하는 경우가 많아 예상치 못한 결과를 초래하게 된다.

이런 문제점을 보완하기 위해 TypeScript라는 언어가 등장하게 되었다.


TypeScript를 사용했을 시 장점

  • TypeScript는 정적타입 검사 기능을 제공.
  • 코드의 가독성과 유지 보수성을 높여준다.
    • 이를 통해 개발자는 런타임 에러를 최소화,
    • 코드 작성 시간을 단축,
    • 협업 시 코드의 가독성을 높일 수 있다.
  • TypeScript는 ES6의 문법을 포함한 최신 JavaScript 문법을 지원
  • 인터페이스(Interface), 제네릭(Generic), 데코레이터(Decorators) 등의 기능을 제공
    • 객체 지향 프로그래밍을 보다 쉽게 할 수 있도록 도와준다.
// 인터페이스(Interface)를 사용하여 코드의 가독성을 높인 예시
interface User {
  id: number;
  name: string;
}

function greetingUser(user: User) {
	 console.log(`Hello, ${user.name}!`)
}

const parkUser = {
	id: 1,
  name: "박해커"
};

greetingUser(parkUser);
  • User 인터페이스를 정의해 User의 정보를 좀 더 쉽게 파악할 수 있다.
  • greetingUser 함수에도 매개변수로 User 타입을 사용해 이 함수가 어떤 타입의 인자를 받고 있는지 명확히 표현하고 있다.

TypeScript를 사용하면 코드의 가독성을 높일 수 있다.

타입을 명시함으로써 코드의 의도 또한 명확해지기 때문에 다른 개발자가 코드를 이해하고 수정하기 쉬워지며, 런타임 에러를 미리 방지할 수 있기 때문에 유지보수성 또한 높아진다.




TypeScript의 타입

TypeScript는 JavaScript와 거의 동일한 데이터 타입을 지원


Boolean(불리언) 타입

참(true), 거짓(false) 값

let isShow: boolean = true;
let isDone: boolean = false;

Number(숫자) 타입

정수와 실수의 구분 없이 Number 타입 하나로 표기

  • TypeScript는 이 외에도 추가로 bigint를 지원
let number1: number = 5;
let number2: number = 0.7;

String(문자열) 타입

큰따옴표(")나 작은따옴표(')를 사용하여 문자열 데이터를 표현

  • 또한 백틱(``)을 사용한 문자열인 템플릿 리터럴을 사용하면 여러 줄에 걸쳐 문자열을 작성할 수 있다.
let firstName: string = "coding";
let lastName: string = 'kim';
let longString: string = `Kimcoding is a developer.
He is 20 years old.`

Array(배열) 타입

두 가지 방법으로 배열 타입을 선언해 사용할 수 있다.

//첫 번째 방법
let items: string[] = ["apple", "banana", "grape"];

//두 번째 방법
let numberList: Array<number> = [4, 7, 100];
  • 첫 번째 방법
    • 배열의 요소들을 나타내는 타입 뒤에 배열을 나타내는 []을 쓰는 것
  • 두 번째 방법
    • 제네릭 배열 타입을 사용하는 것
    • Array를 먼저 작성한 뒤, <> 안에 배열의 요소들을 나타내는 타입을 작성

Tuple(튜플) 타입

튜플 타입을 사용하면 요소의 타입과 개수가 고정된 배열을 표현할 수 있다.

let user: [string, number, boolean] = ["kimcoding", 20, true];

// 모든 요소가 전부 같을 필요는 없지만,
// 배열의 index마다 타입이 정해져 있기 때문에 정확한 index에 접근할 필요가 있다.

console.log(user[2].toString());
// 이렇게 user[2]에 접근하게 되면,
// user[2]에 있는 요소는 boolean 타입이기 때문에 타입 에러가 발생.
  • JavaScript에서도 튜플 타입을 지원하며, JavaScript에서의 튜플 또한 여러 개의 값을 가진 배열을 나타내는 데에 사용한다.
  • 그러나 JavaScript에서는 튜플 타입을 명시적으로 선언할 수 없기 때문에 개발자가 직접 튜플의 각 요소의 타입을 확인하고 유추해야 하므로 타입 에러가 더 쉽게 발생한다.
  • TypeScript의 등장 배경 중 하나인 타입 에러를 방지하고자 한 이유 중 하나이기도 하다.

Object(객체) 타입

JavaScript의 원시 타입에는 number, string, boolean, undefined, null, symbol이 있다.

let obj: object = {};
  • TypeScript에서 object 타입은 모든 객체를 수용하는 타입.
  • 객체의 프로퍼티 타입들이 any로 지정되기 때문에 어떠한 프로퍼티라도 추가할 수 있다.
    • 그러나 이는 타입 안정성을 보장하지 않기 때문에 추천하는 방법은 아니다.
  • 따라서 객체의 프로퍼티 타입들을 각기 명시해 주는 key-value에 구체적인 타입까지도 지정할 수 있다.
let user: {name: string, age: number} = {
	name: "kimcoding",
	age: 20
}

Any 타입

타입 검사를 하지 않고자 할 때 any 타입을 사용할 수 있다.

let maybe: any = 4;

---------------------
  
// any 타입을 사용하게 되면, 변수에 값을 재할당하는 경우, 
// 타입을 명시한 변수와 달리 타입에 구애받지 않고 값을 재할당할 수 있게 된다.

---------------------
  
let obj: object = {};

//에러가 납니다.
obj = "hello";

let maybe: any = 4;

//정상적으로 동작합니다.
maybe = true;

---------------------

// 또한 엄격한 타입 검사를 진행하지 않기 때문에,
// 실제 할당된 값이 가지지 않는 메서드 및 프로퍼티로 접근해도 에러가 나지 않는다.
// 대신, 실제 할당된 값이 가지지 않는 메서드 및 프로퍼티이기 때문에 반환되는 값은 undefined입니다.

---------------------
  
let maybe: any = 4;

//undefined로 출력됩니다.
console.log(maybe.length);

---------------------

// 또한 any 타입은 타입의 일부만 알고, 전체는 알지 못할 때 유용.
// 예를 들어서 여러 타입이 섞인 배열을 받고자 할 때 유용.
  
---------------------
let list: any[] = [1, true, "free"];

//any로 다루고 있기 때문에 index 1번째 요소가 boolean 타입이지만 number 타입으로 재할당할 수 있습니다. 
list[1] = 100;


TypeScript의 함수

JavaScript에서의 함수와 마찬가지로 TypeScript에도 함수는 JavaScript와 마찬가지로 기명 함수(named function)와 화살표 함수(arrow function) 등으로 만들 수 있다.

  • JavaScript에서 함수

    • //named function
      function add(x, y){
      	return x + y;
      }
      
       //arrow function
      let add = (x, y) => {
      	return x + y;
      }
  • 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에서 함수를 표현할 때는 매개변수의 타입과 리턴값의 타입을 명시

    • 각 매개변수에 해당하는 타입을 작성하고, 리턴값의 타입을 괄호 뒤에 작성

    • 반환되는 타입은 타입추론을 이용하여 생략할 수도 있다.

    • //named function
      function add(x: number, y: number) {
      	return x + y;
      }
      
       //arrow function
      let add = (x: number, y: number) => {
      	return x + y;
      }
  • 타입추론 : 리턴값을 작성하지 않아도 TypeScript 컴파일이 스스로 판단해서 타입을 넣어 줌
  • 만약 함수에 리턴값이 없다면, void를 사용하여 작성할 수 있다.
    • let printAnswer = (): void => {
      	console.log("YES");
      }
  • 또한 TypeScriptJavaScript와 달리 매개변수의 개수에 맞춰 전달인자를 전달해야 한다.

    • let greeting = (firstName: string, lastName: string): string => {
      	return `hello, ${firstName} ${lastName}`;
      }
      
       //에러가 납니다.
      greeting('coding');
      
       //정상적으로 작동합니다.
      greeting('coding', 'kim');
      
       //너무 많은 매개변수를 보내 에러가 납니다.
      greeting('coding', 'kim', 'hacker');
  • 만약 개발자가 전달인자를 전달하지 않거나, undefined를 전달했을 때 할당될 매개변수의 값을 정해놓을 수도 있다.

    • 이는 JavaScript에서의 default parameter와 같은 동작

    • let greeting = (firstName: string, lastName: string ="kim"): string => {
      	return `hello, ${firstName} ${lastName}`;
      }
      
       //정상적으로 작동합니다. 
      greeting('coding');
      
       //정상적으로 작동합니다.
      greeting('coding', undefined);
      
       //너무 많은 매개변수를 보내 에러가 납니다.
      greeting('coding', 'kim', 'hacker');
      // 때는 뒤의 인자로 undefined를 보내도 값은 “hello, coding kim”으로 반환
  • 혹은 선택적 매개변수를 원한다면 매개변수의 이름 끝에 물음표(?)를 붙임으로써 해결할 수도 있다.

    • let greeting = (firstName: string, lastName?: string): string => {
      	return `hello, ${firstName} ${lastName}`;
      }
      
       //정상적으로 작동합니다.
      greeting('coding');
      
       //정상적으로 작동합니다.
      greeting('coding', 'kim');
      
       //너무 많은 매개변수를 보내 에러가 납니다.
      greeting('coding', 'kim', 'hacker');
      // 그러나 이때는 greating('coding')과 같이 전달인자를 하나만 전달했기 때문에, 뒤의 매개변수는 undefined로 반환



TypeScript의 연산자 활용 타입

TypeScript는 연산자를 이용해 타입을 정할 수 있다.

  • | 연산자를 이용한 타입을 유니온(Union) 타입
  • & 연산자를 이용한 타입은 인터섹션(Intersection) 타입

유니온(Union) 타입

유니온 타입은 둘 이상의 타입을 합쳐서 만들어진 새로운 타입

  • | 연산자를 이용
  • 자바스크립트의 || (OR) 연산자와 같이 “A이거나 B이다”라는 의미의 타입
    • 예를 들어, number | string은 숫자 또는 문자열 타입을 의미
function printValue(value: any): 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
  • 코드는 value 매개변수의 타입을 any로 정의
  • 타입이 number인지 string인지에 따라 if-else 문으로 나누어 출력

any를 사용하는 것은 JavaScript로 작성하는 것과 큰 차이가 없기 때문에,
유니온 타입을 사용해 TypeScript의 이점을 살리면서 코딩하는 것이 좋다.


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

유니온(Union) 타입의 장점

  • 유니온 타입을 사용하면 타입을 추론할 수 있기 때문에, 타입에 관련된 API를 쉽게 자동완성으로 얻어낼 수 있다.
    • 그러나 any 타입을 사용하면 타입을 추론할 수 없어, 자동완성 기능을 사용하기가 어렵다.
  • 또한 코드의 가독성을 높일 수 있다.
    • let value: string | number | boolean;
    • 이렇게 string | number | boolean 타입으로 선언된 변수는 문자열, 숫자, 불리언 타입 중 하나의 값을 가질 수 있다는 것이 명시적으로 표시되어 코드를 이해하기 쉽게 만들어 준다.

유니온(Union) 타입 사용 시 유의할 점

유니온 타입인 값이 있으면, 유니온에 있는 모든 타입에 공통인 멤버들에만 접근할 수 있기 때문에 유의해야 한다.

  • 인터페이스를 사용하여 DeveloperPerson을 정의

    • interface Developer {
        name: string;
        skill: string;
      }
      
       interface Person {
        name: string;
        age: number;
      }
  • function askSomeone(someone: Developer | Person) {
    	console.log(someone.name);
    }
  • 그러나 실질적으로 askSomenone 함수 내부에서는 DeveloperPerson이 갖고 있는 공통 프로퍼티인 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) 타입

인터섹션(Intersection)은 둘 이상의 타입을 결합하여 새로운 타입을 만드는 방법

  • & 연산자를 사용하여 표현
interface Developer {
  name: string;
  skill: string;
}

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

type User = Developer & Person;
  • 타입을 결합해 사용할 수 있다.
    • 여기서 User 변수는 Developer, Person 각각에 정의된 속성 모두를 받게 된다.
  • 인터섹션으로 타입을 연결해 하나의 단일 타입으로 표현할 수 있기 때문에, 타입 가드가 필요하지 않다.
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);
}
  • 위의 코드는 인터섹션 타입을 사용하여 DeveloperPerson을 하나의 타입으로 묶었다.
    • 따라서 askSomeone 함수 내에선 정의된 프로퍼티에 전부 접근할 수 있다.
  • 그러나 인터섹션 타입은 타입 가드는 필요 없는 반면 DeveloperPerson이라는 새로운 교집합을 만들어 내는 것이기 때문에, 전달인자를 전달할 때 모든 프로퍼티를 전부 보내줘야만 한다.

  • 반대로 유니온 타입은 타입 가드를 해줘야 하지만 전달인자를 전달할 때 선택지가 생기게 된다.

    • 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});
profile
Drop the Bit!

1개의 댓글

comment-user-thumbnail
2023년 7월 26일

공감하며 읽었습니다. 좋은 글 감사드립니다.

답글 달기