TypeScript - Handbook(TypeScript for JS Programmers)

2021. 5. 26. 17:34프론트엔드/Typescript

728x90

탑툰에서 제시한 첫번째 우대사항인 TypeScript를 공부하기로 했다.
사실 회사에서 몇번 써봐서 알기는 하지만, 제대로 알고 쓸 수 있게 연습해보기로 했다.


TypeScript란?

TypeScript는 JavaScript 기반의 오픈소스 언어다. 즉, 컴파일하면 Javascript로 변환된다.
형태도 사실 Javascript와 거의 동일하다.

JavaScript의 가장 큰 문제점은 신뢰성이다. 오브젝트에 멤버가 있건 없건 호출이 가능하기 때문에,
런타임 도중에 에러가 생길 수 있다.
TypeScript는 Type을 명시하게 하여 더 안정성 있게 작업할 수 있게 한다.


설치 방법

사이트에 나온 방법은 NPM을 이용하는 것이다.

나는 npm으로 설치하였다. 

글로벌 설치도 가능하다.

https://www.npmjs.com/package/typescript

 

typescript

TypeScript is a language for application scale JavaScript development

www.npmjs.com

npm install -g typescript

TypeScirpt 배우기

https://www.typescriptlang.org/docs/handbook

 

The starting point for learning TypeScript

Find TypeScript starter projects: from Angular to React or Node.js and CLIs.

www.typescriptlang.org

TypeScript 사이트에는 다음과 같이 공부할 수 있게 핸드북이 있다.
나는 그 중에서 JS 프로그래머를 위한 핸드북을 봤다.

설명에도 나오지만 TypeScript는 Javascript의 한 레이어 위에 쌓여있기 때문에, Javascript의 모든 기능을 지원한다.
다만 Javascript에서 체크하지 않는 타입 체크를 TypeScript 레이어에서 체크한다.


다양한 기초 문법들

우선 기본적인 변수 선언이다.

let name = "inho";			// Javascript
let name:string = "inho";	// Typescript

다음은 오브젝트다.

// Javascript
const user = { "a": "abc", "b": 5 };

// TypeScript
interface User { "a": string; "b": number; };
const user: User = { "a": "abc", "b": 5 };

인터페이스를 이용하여 타입을 선언하고, 선언된 타입을 이용하여 변수를 선언한다
뭐야 이거 구조체잖아 그냥 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

다음은 interface X class다. 이거는 몰랐던 건데, 멤버 형식과 이름이 같다면 다음과 같이 사용할 수 있다.

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

class UserAccount { 
  name: string;
  id: number;

  constructor(name: string, id: number) {
    this.name = name;
    this.id = id;
  }
}

const user: User = new UserAccount("Murphy", 1);

이게 본건데, type선언할 때, 뒤에 ;를 붙여야 한다. { name: string; }

다음은 함수의 매개변수, 반환값에서의 활용이다.

function getAdminUser(): User {
  //...
}

function deleteUser(user: User) {
  // ...
}

사이트에 있는 내용인데, Javascript에 있는 기본 type(primary types)를 포함한 여러가지 새로운 type들이 있다고 한다.
void, any, never 등등이 있다.

다음은 Union과 Generics Types다.
Union은 몇가지 값들이나 Type들 중에 하나를 가질 수 있다.
Generics는 내부에서 특정 타입을 이용할 때 사용할 수 있다.

// Unions
type MyBool = true | false;

function getLength(obj: string | string[]) {
  return obj.length;
}

// Generics
interface Backpack<Type> {
  add: (obj: Type) => void;
  get: () => Type;
}

declare const backpack: Backpack<string>;

// 우리가 .get()의 반환값은 Type이라고 정의했는데, 
// backpack의 Generic Type은 string이다.
// object는 string이다.
const object = backpack.get();

콘솔에서 TypeScript를 즐겨보자

일단 다음과 같이 패키지를 설치한다.

npm install -g typescript
npm install -g ts-node
npm install -g @types/node

ts-node는 타입스크립트 실행 커맨드이고, @type/node는 Type을 구성하기 위한 패키지다.

다음과 같이 코드를 짜주고, 확장자는 .ts라고 해준다.

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

const user: User = {
    name: "Hayes",
    id: 0,
};

console.log(user);

그 다음, 콘솔창에서 실행해준다.

ts-node file-name.ts


Structual Type System

이건 따로 파트를 나누는게 좋다고 생각했다.
TypeScript의 가장 핵심적인 부분인데, 직접 Type을 명시하지 않더라도,
형식을 파악해서 어떤 Type인지 파악하는 시스템이다.

interface Point {
  x: number;
  y: number;
}

function logPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}

// logs "12, 26"
const point = { x: 12, y: 26 };
logPoint(point);

자, logPoint는 Point 타입만을 인자로 받는다.
하지만 가장 아랫줄에서 point 변수를 인자로 넘겼다.
point는 type을 정하지 않았지만, TypeScript는 구조를 파악하여 point변수가 Point라고 파악한 것이다.

근데 이게 골때리는 이유가 있다.

const point3 = { x: 12, y: 26, z: 89 };
logPoint(point3); // logs "12, 26"

const rect = { x: 33, y: 3, width: 30, height: 80 };
logPoint(rect); // logs "33, 3"

구조를 파악해서 어떤 타입을 "포함"하기만 하면 인식한다는 소리다.
(ㄹㅇ 골때림)

그래서 이거는 눈으로만 보지 말고 직접 해봐야겠다고 생각했다.

interface Point {
    x: number;
    y: number;
  }
  
  function logPoint(p: Point) {
    console.log(`${p.x}, ${p.y}`);
  }
  
  // logs "12, 26"
  const point = { x: 12, y: 6, z: 27 };
  logPoint(point);

놀랍게도 z값을 넣었음에도 logPoint로 적용이 되었다.

그렇다면 클래스에도 적용이 될까?

interface Point {
    x: number;
    y: number;
}

class VirtualPoint {
    x: number;
    y: number;

    constructor(pt: Point) {
        this.x = pt.x;
        this.y = pt.y;
    }

    logPoint(p: Point) {
      console.log(`${p.x}, ${p.y}`);
    }
}

const newPoint3D = { x: 3, y: 4, z: 5 };
newPoint3D.logPoint();

newPoint3D를 선언했고, newPoint3D가 같은 형식을 가진 VirtualPoint로 인식이 되는지 확인했다.
결론은 "안 된다"였다. constructor의 내부 코드를 제거했지만, 여전히 불가능했다.

그래서 결론은 클래스 객체를 인터페이스 형태로 사용하는건 가능하지만,
인터페이스 형태를 클래스로 올려치는건 불가능 했다.

그래서 다음과 같이 정리를 했다.

interface Point {
    x: number;
    y: number;
}

class VirtualPoint {
    x: number;
    y: number;
    z: number;

    constructor(x: number, y: number, z: number) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

function logPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}

// Type을 선언하는 경우
const pointWithType: Point = { x: 3, y: 4 };
logPoint(pointWithType);

// Type을 직접 선언하지 않았지만, 형식상 포함될 때
const pointWithoutType = { x: 3, y: 4, z: 5 };
logPoint(pointWithoutType);

// 객체를 생성한 다음 같은 형식의 interface로 사용하는 경우
const pointWithClass = new VirtualPoint(4, 5, 6);
logPoint(pointWithClass);

보면 알겠지만, VirtualPoint는 x, y말고도 z라는 형식을 가지고 있지만,
x, y를 포함하기때문에 Point로 사용이 가능하다.