TypeScript (1) - 타입, 함수, 연산자

Kim-DaHam·2023년 5월 30일
0

JavaScript

목록 보기
16/18
post-thumbnail

🔥 학습목표

  • TypeScript 프로젝트를 생성할 수 있다.
  • TypeScript의 사용 이유를 설명할 수 있다.
  • TypeScript의 타입, 함수, 연산자를 직접 작성할 수 있다.



🟩 TypeScript

JavaScript에 정적타입 검사와 클래스 기반 객체 지향 프로그래밍 등의 기능을 추가하여 개발된 언어다.

└▷ 마이크로소프트에서 개발한 JavaScript의 상위 집합(Superset) 언어이다.

└▷ JavaScript가 발전하면서 생긴 단점을 보완하기 위해 등장하게 되었다.

🟣 등장 배경

⬜ JavaScript의 단점

  • JavsScript의 동적 타입은 유연하고 편리하지만, 타입의 명시성이 부족하다.

  • 타입의 명시성이 정확하지 않은 경우, 자동 형변환이 일어나 1 + "2" = "12" 와 같은 연산을 멋대로 수행한다.


⬜ TypeScript의 장점

위와 같은 문제를 해결하기 위해

  • 정적 타입 검사 기능을 제공한다.

  • 코드의 가독성과 유지 보수성을 높여준다.


아래는 인터페이스를 활용한 TypeScript 사용 예시다.

interface User {
  id: number;
  name: string;
}

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

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

greetingUser(parkUser);

User 객체가 어떤 타입의 프로퍼티를 가지고 있는지 명시적으로 드러난다. greetingUser 함수 또한 어떤 타입의 매개변수를 인자로 받는지 정확히 명시한다.



🟩 TypeScript 실습

🟣 프로젝트 생성하기

⬜ 프로젝트 폴더 생성

프로젝트 폴더를 생성한다.

/* mkdir (폴더명) */
mkdir typescript-learn
/* cd (폴더명) */
cd typescript-learn

⬜ 프로젝트 초기화

npm init -y

명령어를 실행하면 pakage.json 파일이 생성된다. 이제 프로젝트 내부에서 npm을 사용할 준비가 된 것이다.


⬜ TypeScript 설치

npm install typescript --save-dev

프로젝트가 생성되면 루트 디렉토리에 tsconfig.json 파일을 생성한다.

이 파일은 TypeScript 파일들을 JS 파일로 변환할 때 어떤 방식으로 변환할 것인지 세부 설정을 하는 용도다.

{
    "compilerOptions": {
      "target": "es6", // 어떤 버전의 자바스크립트로 바꿔줄 것인지 정한다.
      "module": "commonjs", // JS파일 간 import 문법을 구현할 때 어떤 문법을 사용할 것인지 정한다.
      "sourceMap": true, // 빌드 시 map 파일을 생성할 것인지에 대한 여부.
      "outDir": "./dist" // JS 파일의 아웃풋 경로
    },
    "include": [
      "src/**/*"
    ]
  }
  • module - commonjsrequire을 사용, es2015' 'esnextimport 문법을 사용한다.

  • SourceMap - 코드 상의 위치를 기억하고 빌드 전 어떤 파일, 문장에서 오류가 났는지 확인할 수 있다. 자세한 설명은 여기


🎁 그 외 항목들 참고


⬜ ESLint 및 Prettier 설치

ESLint 설치

  • vsCode 확장 프로그램 ESLint 를 설치한다.
  • 윈도우의 경우 ctrl+shift+p, 맥OS의 경우 cmd+shift+p 를 눌러 명령 팔레트로 이동한다.
  • 사용자 설정을 누른다.사용자 설정 화면
  • 아래 내용을 사용자 설정에 넣는다.
    {
     // ... 
     "editor.codeActionsOnSave": {
         "source.fixAll.eslint": true
     },
     "eslint.alwaysShowStatus": true,
     "eslint.workingDirectories": [
         {"mode": "auto"}
     ],
     "eslint.validate": [
         "javascript",
         "typescript"
     ],
    }
  • VSCode 에디터 설정 중 format on save 가 설정되어 있는지 확인하고, 설정을 해제한다.



Prettier 설치

  • Prettier 확장 프로그램을 설치한다.

  • 추가로 필요한 프리셋과 라이브러리를 설치한다.

npm i -D @babel/core @babel/preset-env @babel/preset-typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint prettier eslint-plugin-prettier
  • 프로젝트 폴더 밑에 .eslintrc.js 파일을 만들고 아래 코드를 붙여 넣는다. prettiertypescript-eslint, React 에 대한 설정 파일이다. (개발자의 취향에 따라 작성하면 된다.)
module.exports = {
  root: true, // 본 파일이 설정 파일이라는 걸 의미. 설정 파일을 찾기 위해 더 이상 상위 파일로 올라가지 않음.
  env: { // ESLint는 미리 선언하지 않고 접근하는 변수에 대해 오류를 내기 때문에 기본 제공되는 전역 객체에 대해서 알려준다.
    browser: true,
    node: true,
    jest: true,
  },
  extends: [
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:@typescript-eslint/recommended',
  ], // 추천 설정을 사용
  plugins: ['prettier', '@typescript-eslint'], // ESLint에서 기본으로 제공되는 규칙 외 추가적인 규칙 사용
  rules: { // 규칙을 세세하게 제어. extends 옵션을 통해 설정된 규칙을 덮어쓰고 싶을 때 잘 쓰인다.
    'prettier/prettier': [
      'error',
      {
        singleQuote: true,
        tabWidth: 2,
        printWidth: 80,
        bracketSpacing: true,
        arrowParens: 'avoid',
      },
    ],
    '@typescript-eslint/no-explicit-any': 'off', // 
    '@typescript-eslint/explicit-function-return-type': 'off',
    'prefer-const': 'off',
  },
  parserOptions: {// JS의 확장 문법이나 최신 문법으로 작성한 코드를 린트(lint)하기 위해서는 그에 상응하는 parser를 사용해야 한다.
    parser: '@typescript-eslint/parser',
  },
};

🎁 더 자세한 옵션 설명은 여기


⬜ ts 파일 작성하기

이제 src 폴더 밑에 index.ts 등과 같은 .ts 파일을 만들어서 TypeScript 코드를 작성할 수 있다.


⬜ ts 파일을 js로 컴파일

컴파일이란, 소스코드를 특정 플랫폼에서 실행 가능한 형태로 변환하는 과정을 뜻한다.

브라우저에서 실행되는 자바스크립트는 전통적으로 컴파일이 필요 없는 인터프리터 언어다.

브라우저나 Node.js는 자바스크립트 코드를 그대로 이해하고 바로 실행할 수 있다.


하지만 타입스크립트는 어떨까?

타입스크립트로 작성한 코드는 자바스크립트로의 컴파일 과정이 필요하다.

개발은 타입스크립트로 하지만, 배포는 자바스크립트로 해야한다.

이때 우리는 타입스크립트 컴파일러(TypeScript compiler)를 사용한다.


컴파일러 실행 방법은 다음과 같다.

위에서 환경설정을 할 때 npm 을 통패 타입스크립트 컴파일러를 설치했었다.

만약 hello.ts 타입스크립트 파일을 작성했다면

npx tsc hello.ts

다음과 같이 tsc 커맨드를 사용하여 hello.ts 파일을 자바스크립트로 변환한다.

컴파일 결과 동일한 디렉토리에 hello.js 가 생긴 걸 볼 수 있다.

🎁 타입스크립트 컴파일러



🟣 TypeScript: 타입

⬜ Boolean

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

⬜ Number

let number1: number = 5;
let number2: number = 0.7;

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


⬜ String

let firstName: string = 'hello';

⬜ Array

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

//두 번째 방법
let numberList: Array<number> = [4, 7, 100];

배열 타입은 기본적으로 하나의 타입만 작성하게 되어 있으며 혼용해서 작성하는 것은 불가능하다.


⬜ Tuple

요소의 타입과 개수가 고정된 배열을 표현한다.

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

특정 타입이 정해진 요소에 해당 타입과 호환되지 않는 접근을 할 경우 에러가 발생한다.

// 에러발생
console.log(user[2].toString());

⬜ Object

TypeScript에서 객체란 JS와 마찬가지로 원시 타입(number, string, boolean, undefined, null, symbol)이 아닌 타입을 뜻한다.

let obj: object = {};

TypeScript에서 object 타입은 모든 객체를 수용하는 타입이다. 따라서 객체의 프로퍼티 타입을 별도로 지정해주지 않으면 any 로 지정되고 만다.

이는 타입의 안정성을 보장하는 TypeScript의 목적과 맞지 않기 때문에 객체의 프로퍼티 타입을 각기 명시해주어야 한다.

let user: {name: string, age: number} = {
	name: "kimcoding",
	age: 20
}

⬜ Any

알지 못하는 타입을 표현할 때 사용한다.

유저→클라이언트 로 받은 데이터나 서드파티 라이브러리에서 들어오는 값은 개발자가 먼저 유추하기 어렵다.

이때 타입 검사를 하지 않기 위해 any 를 사용한다.

let maybe: any = 4;

any 타입은 변수를 재할당 할 때 타입에 구애받지 않고 받을 수 있다.

//에러 발생
let obj: string = "hello";
obj = 1;

//정상적으로 동작
let maybe: any = 4;
maybe = true;

또한 여러 타입이 섞인 배열을 받고자 하는데, 타입의 일부만 알고 전체를 알지 못할 때 any 를 사용하면 좋다.

let list: any[] = [1, true, "free"];
list[1] = 100;

any이기 때문에 1번째 요소가 boolean 타입이더라도 number 타입으로 재할당할 수 있다.



🟣 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;
}

만약 리턴 값이 없을 경우 void 를 사용한다.

let printAnswer = (): void => {
	console.log("YES");
}

⬜ 매개변수 개수 맞추기

let greeting = (firstName: string, lastName: string): string => {
	return `hello, ${firstName} ${lastName}`;
}

// 에러(매개변수 부족)
greeting('coding');
// 정상 작동
greeting('coding', 'kim');
// 에러(매개변수 과잉)
greeting('coding', 'kim', 'hacker');

⬜ 초기값 설정 및 선택적 매개변수

🔵 매개변수의 초기값 설정

let greeting = (firstName: string, lastName="kim"): string => {
	return `hello, ${firstName} ${lastName}`;
}

// 정상 작동 
greeting('coding');

// 정상 작동
greeting('coding', undefined);

// 에러(매개변수 과잉)
greeting('coding', 'kim', 'hacker');

🔵 선택적 매개변수

매개변수 이름 끝에 ? 를 붙이면 된다.

let greeting = (firstName: string, lastName?: string): string => {
	return `hello, ${firstName} ${lastName}`;
}

// 정상 작동
greeting('coding');

// 정상 작동
greeting('coding', 'kim');

// 에러(매개변수 과잉)
greeting('coding', 'kim', 'hacker');



🟣 TypeScript: 연산자

⬜ 유니온(Union) 타입

둘 이상의 타입을 합쳐서 만들어진 새로운 타입이다. (feat. 합집합)

  • | 연산자를 사용한다.
  • number | string : 숫자 또는 문자열 타입
  • 다양한 타입의 값을 처리해야 하는 경우 유용.

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);
printValue("hello");



🔴 단, 유니온 타입 사용 시 주의할 점이 있다.

두 객체를 유니온 타입으로 묶는다면, 유니온에 있는 모든 타입에 공통적인 멤버들에만 접근할 수 있다.

interface Developer {
  name: string;
  skill: string;
}

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

위와 같이 두 인터페이스 Developer, Person 을 정의했다면,

두 객체가 동시에 갖고 있는 name 프로퍼티ㅔ만 접근할 수 있는 것이다.

만약 다른(공통되지 않은) 프로퍼티에 접근하고 싶다면 Type Guard 를 사용해야 한다.

타입 가드(Type Guard)란?
TypeScript에서 타입을 보호하기 위해 특정 코드 블록에서 타입의 범위를 제한해 해당 코드 블록 안에서 타입 안정성을 보장해주는 것.

function askSomeone(someone: Developer | Person) {
  if ('skill' in someone) // Type Guard
    console.log(someone.skill);

  if ('age' in someone) // Type Guard
    console.log(someone.age);
}



실습

type Person = {
  name: string;
  age: string | number;
};

function printAge(person: Person) {
  let age;
  if (typeof person.age === 'number' || typeof person.age === 'string') {
    age = person.age.toString();
  }
  console.log(`${person.name}의 나이는 ${age}살 입니다.`);
}

const kimcoding = {
  name: '김코딩',
  age: 30,
};

const parhacker = {
  name: '박해커',
  age: '서른',
};

printAge(kimcoding);
printAge(parhacker);



⬜ 인터섹션(Intersection) 타입

둘 이상의 타입을 결합하여 새로운 타입을 만드는 방법

  • & 연산자를 사용한다.

  • string & number & boolean : 변수는 string, number, boolean 타입을 전부 받을 수 있다.

  • 인터섹션으로 타입을 연결해 하나의 단일 타입으로 표현한다.

  • 따라서 타입 가드가 필요 없다.

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); // 가능
}



실습

interface User {
  name: string;
  email: string;
}

interface Admin {
  name: string;
  isAdmin: boolean;
}

function sendEmail(user: User & Admin) {
  console.log(`안녕하세요, ${user.name}!`);
  if (user.isAdmin) {
    console.log(
      `
      권한이 admin이시군요.
      이메일은 ${user.email} 입니다.
      `
    );
  } else {
    console.log(
      `
      권한이 user이시군요.
      이메일은 ${user.email} 입니다.
      `
    );
  }
}

const kimcoding = {
  name: '김코딩',
  email: 'kimcoding@codestates.com',
  isAdmin: false,
};

const parkhacker = {
  name: '박해커',
  email: 'parkhacker@codestates.com',
  isAdmin: true,
};

sendEmail(kimcoding);
sendEmail(parkhacker);



🔵 유니온(Union) 연산자와의 차이점

  • 두 타입의 새로운 교집합을 만들어 내는 것이기 때문에, 전달 인자를 전달할 때 모든 프로퍼티를 전부 보내줘야 한다.

  • 반면 유니온 타입은 타입가드를 해주는 대신 전달인자를 선택하여 전달할 수 있다.

// Union
function askSomeone(someone: Developer | Person) {
  ...
}
  
askSomeone({name: '김코딩', skill: '웹 개발'});
askSomeone({name: '김코딩', age: 20}); // 선택해서 전달할 수 있다.
  
  
// Interaction
function askSomeone2(someone: Developer & Person) {
	...
}

askSomeone2({name: '김코딩', skill: '웹 개발', age:20}); // 무조건 다 전달해야 한다.



🎁 SDS 타입스크립트 리포트

profile
다 하자

0개의 댓글