TypeScript

9 minute read

TypeScript

TypeScript는 JavaScript의 superset입니다. TypeScript는 새로운 언어가 아니기에 브라우저 또는 nodejs에서 실행되지 않습니다. 다만, type을 정의해주기에 runtime error를 미리 방지할 수 있어 개발에 도움을 줄 수 있습니다.

환경설정

• 설치하기

npm을 통해 typescript를 설치합니다.

npm install --save-dev typescript

• compile

tsc [file]로 typescript에서 javascript로 compile합니다. 또한 tsc --init를 실행하여 tsconfig.json에 compile 환경을 설정합니다.
🔎 ts-node 패키지를 통해 typescript를 nodejs에서 편리하게 사용할 수 있습니다.
"start": tsc & nodemon -e ts --exec ts-node server.ts"

// tsconfig.json
{
  "compilerOptions": {
      "strict": true,
      "target": "ES6",
      "lib": ["ES2015", "DOM"],
      "module": "CommonJS"
  }
  "exclude": [
    ***.ts
    *.dev.ts
    node_modules
  ],
  "include": [
    "src/**/*.ts"
  ]
}

static file https://vccolombo.github.io/blog/tsc-how-to-copy-non-typescript-files-when-building/

Types

typescript는 타입을 정의하여, 정적으로 타입오류를 찾아주어 개발을 편리하게 해줍니다. 이때 보통 변수선언(var/const/let), 함수선언(매개변수, 반환값) 시 타입을 지정해 줍니다.

• 기본 Type

typescript는 기본 type으로 number, string, boolean, object, array, tuple, enum, any, union, literal을 가지고 있으며, : type으로 타입을 정의해줍니다.
🔎 typescript는 변수 초기화 시, 별도로 : type을 하지 않아도 타입을 지정합니다.

- number
숫자 type입니다.(정수, 실수를 구분하지 않습니다.)

let a: number;

- string
문자열 type입니다.

let a: string;

- boolean
불리언(true/false) type입니다.(truthy, falsy는 해당되지 않습니다.)

let a: boolean;

- object
객체 type입니다.

const obj: {
  a: string,
  b: number
} = {
  a: 'hello',
  b: 1
}

- array
배열 type입니다.

const a: string[] = ["hello", "typescript"];
const a: Array<string> = ["hello", "typescript"];
const c: any[] = ["hello", 1];

- tuple
튜플(고정된 길이의 배열) type입니다.
🔎 tuple은 push/pop/unshift/shift/splice는 검사하지 않습니다.

const a: [number, string] = ["hello", 1];
a[2] = "typescript"; //error
a.push(3); // non error

- enum
열거형 type입니다.
🔎 코드 가독성을 위해 enum을 쓰며, 초기화를 하지않을 경우 0부터 오름차순으로 초기화됩니다.

enum A { p = 'hello', q = 'typescript' };

- any
지정되지 않은 type입니다.

const a: any

- unknown
지정되지 않은 type입니다. 🔎 any처럼 모든 type을 받을 수 있지만 지정된 typescript형식의 변수를 담는건 안되며 type을 개발자가 지정하여 사용해야 합니다. 또한 객체 key에

let a: unknown;
let b: number = 1;
a = 1; // non error
a = "hello"; //non error
a = b; // error

if (typeof a === "string") {
  a.indexOf("k");
}

- union
조합된 type입니다.

let a: number | string;
a = 1;
a = "hello";

- intersection
type을 조합하여, 또 다른 type을 정의합니다.

type A = {
  k: string,
  p: string,
  q: number,
};

type B = {
  k: string,
  m: string,
  n: boolean,
};

type C = A & B;

const c: C = {
  k: "typescript",
  p: "hello",
  q: 1,
  m: "typescript",
  n: true,
};

- literal
literal type입니다.

let a: "hello" | 3 | "typescript";
a = "hello";
a = 3;
a = "typescript";

- never
절대 생기지 않는 값의 type입니다. 일반적으로 함수가 return으로 끝나지 않고 throw로 끝날때 정의합니다.

function func(): never {
  throw new Error();
}

function func(): void {
  throw new Error();
}

- Optional 해당 변수가 있거나 없거나의 type을 선택적으로 정의합니다.

interface M {
  a: string;
  b?: string;
}

const c: M = {
  a: "hello",
};

- TypeGuard - !

• Type Alias

type을 alias할 수 있습니다.

type A = number | string;
let b: A;

• Function Type

- 매개변수/반환값 type
함수 매개변수/반환값에 type을 지정하여 함수 type을 정의합니다..

let a: Function;
a = function () {};
let a: (m: number, n: true) => string;
a = function (p, q) {
  if (n) {
    return m.toString();
  }

  return "hello";
};
function func(a: number, b: number): number {
  return a + b;
}

function func(a: number, b: number): void {
  console.log("hello typescript");
}

function func(a: number, b: number): void {
  return;
}

// 사용하지 않는 형태
function func(a: number, b: number): undefined {
  return;
}
function func(m: string, cb: (n: string) => void): void {
  cb(m.toString());
}

Class & Interface

• Class

typescript는 class를 type으로 가질 수 있습니다.

class Vehicle {
  color: string;
  constructor(color: string) {
    this.color = color;
  }
}

const vehicle: Vehicle = new Vehicle("red");

console.log(vehicle.color); // non error
console.log(vehicle.wheel); // error

- constructor
Vehicle의 인스턴스를 초기화하기 위해, constructor의 매개변수 및 멤버변수 type을 정의해주어야 합니다.

class Vehicle {
  color: string;

  constructor(color: string) {
    this.color = color;
  }
}

const vehicle: Vehicle = new Vehicle("blue");

- 메소드
this의 type을 정의해주는 것을 제외하고, 함수 type 정의와 같습니다.

class Vehicle {
  ignite(ignition: boolean): void {
    if (ignition) {
      console.log("start");
    }
  }

  inform(this: Vehicle) {
    console.log(this);
  }
}

const vehicle: Vehicle = new Vehicle();
const mockVehicle = {
  inform: vehicle.inform,
};

vehicle.inform(); // non error
mockVehicle.inform(); // error

- 상속
this의 type을 정의해주는 것을 제외하고, 함수 type 정의와 같습니다.

class Vehicle {
  color: string;

  constructor(color: string) {
    this.color = color;
  }

  ignite(ignition: boolean): void {
    if (ignition) {
      console.log("start");
    }
  }

  inform(this: Vehicle) {
    console.log(this);
  }
}

class Car extends Vehicle {
  model: string;
  constructor(color: string, model: string) {
    super(color);
    this.model = model;
  }
}

const carA: Vehicle = new Car("blue", "i30");
const carB: Car = new Car("black", "tesla s");

carA.inform();
carB.inform();

console.log(carA.model); // error
console.log(carB.model); // non error

- public / private / protected
접근권한에 따라 public, private, protected로 변수를 선언할 수 있습니다. public은 외부/같은 클래스/자식 클래스, private는 같은 클래스, protected는 같은 클래스/자식 클래스에서 접근이 가능합니다.

class Vehicle {
  public color: string;
  private _price: number;
  protected _owner: string;

  constructor (color: string, price: number, owner: string) {
    this.color = color;
    this._price = price;
    this._owner = owner;
  }

  get(this: Vehicle) {
    console.log(this.color); // non error
    console.log(this._price); // non error
    console.log(this._owner); // non error
  }
}

class Car extends Vehicle {
  model: string;
  constructor (color: string, price: number, owner: string, model: string) {
    super(color, price, owner);
    this.model = model;
  }
  get(this: Car) {
      console.log(this.color); // non error
      console.log(this._price); // error
      console.log(this._owner); // non error
  }
}

const vehicle: Vehicle = new Vehicle('blue', 1000, 'me');
const car: Car = new Car('blue', 1000, 'me', 'i30');

console.log(vehicle.color) // non error
console.log(vehicle.price) // error
console.log(vehicle.owner) // error

console.log(car.color) // non error
console.log(car.price) // error
console.log(car.owner) // error

- readonly
readonly는 읽기만 가능한 type입니다.

class Vehicle {
  readonly color: string;
  model: string;

  constructor (color: string, model: string) {
    this.color = color;
    this.model = model;
  }
}
const vehicle: Vehicle = new Vehicle('blue', 'i30');

vehicle.model = 'tesla s'; // non error
vehicle.color = 'black'; // error

🔎 public / private / protected / readonly 키워드를 constructor 매개변수에 정의하면, 초기화를 하지 않아도 됩니다.

class Vehicle {
  color: string;
  price: number;
  owner: string;
  model: string;

  constructor (color: string, price: number, owner: string, model: string) {
    this.color = color;
    this.price = price;
    this.owner = owner;
    this.model = model;
  }
}

const vehicle: Vehicle = new Vehicle('blue', 1000, 'me', 'i30);
class Vehicle {
  constructor (public color: string, private price: number, protected owner: string, readonly model: string) {
  }
}

const vehicle: Vehicle = new Vehicle('blue', 1000, 'me', 'i30);

• Interface

객체의 구조(인터페이스) 타입을 정의합니다. type가 유사하지만 명확히 타입을지정할 수 있으며 class interface와 함께 사용될 수 있습니다.

interface Vehicle {
  color: string;
}

const vehicle: Vehicle = { color: "blue" };

- extends
interface는 class처럼 type을 상속받을 수 있습니다.

interface Vehicle {
  color: string;
}

interface Car extends Vehicle {
  model: string;
}

const vehicle: Vehicle = { color: "blue" };
const car: Car = { color: "blue", model: "i30" };

- implements
interface는 class처럼 type을 정의할 수 있습니다. (interface에 없는 멤버, 메소드를 허용합니다.)

interface IVehicle {
  color: string;
}

class Vehicle implements IVehicle {
  color: string;
  model: string; // non error
  constructor(color: string, model: string) {
    this.color = color;
    this.model = model;
  }
}

const vehicle: Vehicle = { color: "blue", model: "i30" };

Type Casting

외부에서 오는 변수처럼, 해당 변수의 type을 모를때 type casting을 합니다.

아래 코드에서 typescript는 $input이 input dom인지 알 수 없기에 오류가 발생합니다.

const $input = document.getElementById("input");
$input.value = "hello"; // error 발생

아래처럼 두가지 방식으로 type casting을 하면 올바른 type을 정의하기에 오류가 발생하지 않습니다.

const $input = <HTMLInputElement>document.getElementById('input');
$input.value = 'hello';
const $input = document.getElementById('input')! as HTMLInputElement;
// null이 아닐때 !를 붙이고, null일 수 있다면 !를 붙이지 않습니다.
$input.value = 'hello';

Generics

Generic은 type의 함수입니다.

• Generic Function 만들기

function merge<T extends object, U extends object>(objA: T, objB: U) {
  return Object.assign(objA, objB);
}

const mergedObj = merge({ name: "jerry" }, { age: "30" });
function func<T extends object, U extends keyof T>(obj: T, key: U) {
  return obj[key];
}
const a = (m: number, n: number): { m: number, n: number } => {
  return {
    m,
    n,
  };
};
const b = <T extends {}>(m: T, n: T): { m: T; n: T } => {
  return {
    m,
    n,
  };
};

• Generic Class 만들기

• Generic Utillity Type

- Partial interface b의 모든 key를 optional type으로 정의합니다.

type Partial<T> = {
	[k in keyof T]?: T[k];
};
interface b = {
  p: string,
  q: number
};
const a: Partial<b> = {};

- Readonly interface b의 모든 key를 optional type으로 정의합니다.

const a: Readonly<string[]> = ["hello", "typescript"];
a.push("^___^"); // error

- Pick 타입 b에서 property를 선택하여 새로운 타입을 만듭니다.

type Pick<T, K> = {
	[k in K]: T[k];
};
interface b = {
  p: string,
  q: number,
  r: boolean
};

const a: Pick<b, 'p' | 'q'>

🔎 Mapped Type type P = { [k in ‘a’ | ‘b’]: T[k]] }; [k in ‘a’ | ‘b’]: T[k]는 map + for in 함수와 비슷합니다. [‘a’, ‘b’].map((k) => T[k])

Decorator

Generic이란…

JavaScript vs TypesScript JavaScript는 동적타입 프로그래밍 언어이기에, 아래와 같이 런타임에 오류가 발생합니다. 반면 TypeScript는 정적타입 프로그래밍 언어이기에 컴파일 시 오류가 발생합니다.

function func(n) {
  if (typeof n !== "number") {
    throw new Error();
  }

  console.log(n);
}

func("3");
function func(n: number) {
  console.log(n);
}

func("3");

타입스크립트 원시형자료형은 소문자입니다. (Number X number O)

타입스크립트에서 변수 선언 시, 초기화를 하지 않으면 any로 지정됩니다.

let n;
n = 3;
n = 5;

반면, 초기화를 하면 할당된 변수에 맞게 타입이 지정되며 아래 두 코드는 같은 코드입니다.

let n = 3;
n = 5;
n '5'; // error
let n: number;
n = 3;
n = 5;
n '5'; // error

https://hyunseob.github.io/2017/12/12/typescript-type-inteference-and-type-assertion/

타입단언

as [type]은 있는 타입중에서 하나로 단언한다는 의미, 아무거나가 아님

const a: A|B = {...}
b = a as A // ok
b = a as C // error

const a: any = {...}
b = a as A // ok
b = a as C // ok

https://joshua1988.github.io/ts/guide/interfaces.html#%EA%B0%9D%EC%B2%B4-%EC%84%A0%EC%96%B8%EA%B3%BC-%EA%B4%80%EB%A0%A8%EB%90%9C-%ED%83%80%EC%9E%85-%EC%B2%B4%ED%82%B9

https://steadily-worked.tistory.com/536?category=935656

https://steadily-worked.tistory.com/530?category=935656

타입스크립트 꿀팀 vscode

import a, {b} from [library]

a.~~~

- 타입 추론 - 타입 호환

타입 단언 / 타입 캐스팅 타입캐스팅은 없는타입으로 형변환(형을 부여하는 것) 타입단언은 있는 타입에서 범위를 좁히는 것. 따라서 런타임에서는 영향 안줌 as https://basarat.gitbook.io/typescript/type-system/type-assertion

https://www.typescripttutorial.net/typescript-tutorial/type-casting/ - 타입 가드 if문으로 타입의 범위를 좁혀감

let a: string | number

function (a: string|number) {
  if (typeof a === 'string') { //instanceof
    a.substring()
  } else {
    Math.sqrt(a)
  }
}

타입가드함수 https://radlohead.gitbook.io/typescript-deep-dive/type-system/typeguard

Cheating Sheet

// destructing
const { a, b } = c;
->
const { a, b }: { a: number, b: string } = c;

// let, type guard
let a: number | undefined

// array
let arr: number[]
arr = []
arr = [1]


http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788966263134

intersection은 속성이 아닌 타입에 대해 & 개념 c = a & b (a와 b를 모두 만족하는 타입 c -> 따라서 a,b 속성을 모두 가짐) c = a | b (a 또는 b를 만족하는 타입 c -> 따라서 a,b 겹치는 속성을 가짐)

https://velog.io/@soulee__/TypeScript-Union-Type

https://ahnheejong.gitbook.io/ts-for-jsdev/03-basic-grammar/intersection-type

Updated: