Top

Up

TypeScript

타입스크립트란?

장점:

타입:

기본 타입은 12가지

string 문자열:

let str: string = 'hi';

number 숫자:

let num: number = 10;

boolean 진위:

let isLoggedIn: boolean = false;

array 배열:

let arr: number[] = [1,2,3];

제네릭:

let arr: Array<number> = [1,2,3];

tuple 튜블

각 요소의 타입이 지정되어 있는 고정 길이 배열:

let arr: [string, number] = ['hi', 10];

정의하지 않은 타입 및 인덱스로 접근하면 오류:

arr[1].concat('!'); // Error, 'number' does not have 'concat'
arr[5] = 'hello'; // Error, Property '5' does not exist on type '[string, number]'.

enum 열거형:

enum Avengers { Capt, IronMan, Thor }
let hero: Avengers = Avengers.Capt;

인덱스 번호로 접근:

enum Avengers { Capt, IronMan, Thor }
let hero: Avengers = Avengers[0];

인덱스 변경:

enum Avengers { Capt = 2, IronMan, Thor }
let hero: Avengers = Avengers[2]; // Capt
let hero: Avengers = Avengers[4]; // Thor

any 모든 타입 허용:

let str: any = 'hi';
let num: any = 10;
let arr: any = ['a', 2, true];

void:

let unuseful: void = undefined;
function notuse(): void {
  console.log('sth');
}

never 함수의 끝에 도달하지 않음

// 이 함수는 절대 함수의 끝까지 실행되지 않는다는 의미
function neverEnd(): never {
  while (true) {
  }
}

함수:

3가지 타입:

함수에서 타입

매개변수와 반환값의 타입 지정:

function sum(a: number, b: number): number {
  return a + b;
}

반환값이 없는 경우void를 반환 타입으로 지정:

function noresult(): void {
  console.log('noresult called');
}

함수의 인자

함수의 인자를 모두 필수 값으로 간주.

함수의 매개변수에는 undefinednull이라도 인자로 전달 필수.

컴파일러는 정의된 매개변수 값이 넘어 왔는지 체크.

function sum(a: number, b: number): number {
  return a + b;
}
sum(10, 20); // 결과: 30
sum(10, 20, 30); // error, too many parameters
sum(10); // error, too few parameters

인자를 선택적으로 전달하고 싶다면 ?를 지정:

function sum(a: number, b?: number): number {
  return a + b;
}
sum(10, 20); // 30
sum(10, 20, 30); // error, too many parameters
sum(10); // 10

매개변수 초기화는 ES6와 동일:

function sum(a: number, b = '100'): number {
  return a + b;
}
sum(10, undefined); // 110
sum(10, 20, 30); // error, too many parameters
sum(10); // 110

REST 문법이 적용된 매개변수

function sum(x, y, z) {
  return x + y + z;
}
const numbers = [1, 2, 3];
sum(...numbers); // expected output: 6
sum.apply(null, numbers); // expected output: 6

Spread 문법으로 두 배열을 합치거나 복사:

var parts = [ 'shoulders', 'knees' ]; 
var lyrics = [ 'head', ...parts, 'and', 'toes' ];
// lyrcis = [ "head", "shoulders", "knees", "and", "toes" ]

ES6에서 Rest문법은 Spread와 비슷하지만 여러 인자들을 하나의 배열로 반환합니다. 함수의 지역인자 arguments와 Rest의 다른 점은 arguments는 모든 인자를 포함하며 유사배열입니다.

function sum(...theArgs) {
  return theArgs.reduce((previous, current) => {
    return previous + current;
  });
}
sum(1, 2, 3); // expected output: 6
sum(1, 2, 3, 4); // expected output: 10

타입스크립트에서 Rest 문법:

function sum(a: number, ...nums: number[]): number {
  const totalOfNums = 0;
  for (let key in nums) {
    totalOfNums += nums[key];
  }
  return a + totalOfNums;
}

this

타입스크립트에서 this:

function 함수명(this: 타입) {
  // ...
}

예를 들면:

interface Vue {
  el: string;
  count: number;
  init(this: Vue): () => {};
}

let vm: Vue = {
  el: '#app',
  count: 10,
  init: function(this: Vue) {
    return () => {
      return this.count;
    }
  }
}

let getCount = vm.init();
let count = getCount();
console.log(count); // expected output: 10

위의 코드를 컴파일 했을 때 만일 --noImplicitThis 옵션이 있더라도 에러 없음.

콜백에서의 this

일반적인 상황에서의 this와 다르게 콜백으로 함수가 전달되었을 때의 this를 구분하여 강제해야 함:

interface UIElement {
  // onclick 매개변수 `this: void`는 `this`를 사용할 수 없습니다.
  addClickListener(onclick: (this: void, e: Event) => void): void;
}

class UIHandler {
    info: string;
    onClick(this: Handler, e: Event) {
        // onclick 매개변수 `this`는 `void`인데 `this`를 사용하여 오류입니다.
        this.info = e.message;
    }
}
let handler = new UIHandler();
uiElement.addClickListener(handler.onClick); // error!

UIElement 인터페이스에 따라 Handler를 구현하면 아래와 같습니다.

class UIHandler {
    info: string;
    onClick(this: void, e: Event) {
        // onclick 매개변수 `this: void`는 `this`를 사용할 수 없습니다.
        console.log('clicked!');
    }
}
let handler = new UIHandler();
uiElement.addClickListener(handler.onClick);

인터페이스

간단한 인터페이스의 예:

간단한 인터페이스의 예:

function printLabel(labeledObj: { label: string }) {
  console.log(labeledObj.label);
}
 
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

타입스크립트에서는 인터페이스를 제공하여 컴파일시 오류 검증:

interface LabeledValue {
  label: string;
}
 
function printLabel(labeledObj: LabeledValue) {
  console.log(labeledObj.label);
}
 
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

인터페이스에서 옵션 속성

초기값 주지 않아도 되는 인터페이스 속성:

interface SquareConfig {
  color?: string;
  width?: number;
}
 
function createSquare(config: SquareConfig): { color: string; area: number } {
  let newSquare = { color: "white", area: 100 };
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}
 
let mySquare = createSquare({ color: "black" });

lint 또는 컴파일러가 오류 탐지:

interface SquareConfig {
  color?: string;
  width?: number;
}
 
function createSquare(config: SquareConfig): { color: string; area: number } {
  let newSquare = { color: "white", area: 100 };
  if (config.clor) {
Property 'clor' does not exist on type 'SquareConfig'. Did you mean 'color'?

    // Error: Property 'clor' does not exist on type 'SquareConfig'
    newSquare.color = config.clor;
Property 'clor' does not exist on type 'SquareConfig'. Did you mean 'color'?

  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}
 
let mySquare = createSquare({ color: "black" });

읽기 전용 속성

readonly를 사용하여 객체 생성시 값을 할당하고 변경할 수 없는 속성.

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

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
 
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

읽기 전용 배열

ReadonlyArray<T> 은 읽기 전용 배열이며 선언시에만 초기자를 줄 수 있음:

let arr: ReadonlyArray<number> = [ 1,2,3 ];
arr.splice(0,1); // 배열 내용을 변경할 수 없으므로 오류.
arr.push(4); // 배열 내용을 변경할 수 없으므로 오류.
arr[0] = 100; // 배열 내용을 변경할 수 없으므로 오류.

imutable을 mutable에 할당은 허용되지 않음:

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
 
a = ro as number[];

readonly를 사용할지 const를 사용할지 기억하는 가장 쉬운 방법은 변수에 사용할지 프로퍼티에 사용할지에 따름. 변수는 const를 사용하는 반면 프로퍼티는 읽기 전용을 사용.

타입 체크

타입스크립트는 인터페이스를 통한 객체 선언에서 엄밀한 속성 검사:

interface SquareConfig {
  color?: string;
  width?: number;
}
 
function createSquare(config: SquareConfig): { color: string; area: number } {
  return {
    color: config.color || "red",
    area: config.width ? config.width * config.width : 20,
  };
}
 
let mySquare = createSquare({ colour: "red", width: 100 }); // colour 오류

타입 추론을 무시하려면 as 사용.

let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);

인터페이스 정의하지 않은 속성들을 추가로 사용:

interface SquareConfig {
  color?: string;
  width?: number;
  [propName: string]: any;
}

let squareOptions = { colour: "red" };
let mySquare = createSquare(squareOptions); // 오류

함수 타입

인터페이스로 함수 타입 정의:

interface SearchFunc {
  (source: string, subString: string): boolean;
}

사용의 예:

let mySearch: SearchFunc;
 
mySearch = function (source: string, subString: string): boolean {
  let result = source.search(subString);
  return result > -1;
};

타입만 체크하므로 동일:

let mySearch: SearchFunc;
 
mySearch = function (src: string, sub: string): boolean {
  let result = src.search(sub);
  return result > -1;
};

오류:

let mySearch: SearchFunc;
 
mySearch = function (src, sub) {
  let result = src.search(sub);
  return "string"; // string을 리턴하므로 오류
};

인덱서블 타입 Indexable Type:

[] 연산자를 사용할 수 있는 타입. 숫자를 인덱스로 주고 문자열을 반환:

interface StringArray {
  [index: number]: string;
}
 
let myArray: StringArray;
myArray = ["Bob", "Fred"];
 
let myStr: string = myArray[0];

인덱스로는 자바스크립트의 레거시로 string, number, symbol, template strings 만 사용가능. 인덱스로 number를 사용하면 자바스크립트는 내부적으로 문자열로 변환하여 사용.

interface Animal {
  name: string;
}
 
interface Dog extends Animal {
  breed: string;
}
 
interface NotOkay {
  [x: number]: Animal; // number 인덱스는 Animal타입으로 Dog 타입으로 할당 불가능.
  [x: string]: Dog;
}
interface NumberDictionary {
  [index: string]: number; 
  length: number; // ok, length is a number
  name: string; // error, the type of 'name' is not a subtype of the indexer
}
interface NumberOrStringDictionary {
  [index: string]: number | string; 
  length: number; // ok, length is a number
  name: string; // ok, name is a string
}

읽기 전용 인덱스:

interface ReadonlyStringArray {
  readonly [index: number]: string;
}
 
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!

Indexable Types with Template Strings

interface HeadersResponse {
  "content-type": string,
  date: string,
  "content-length": string 
  // Permit any property starting with 'data-'.
  [headerName: 'x-${string}']: string;
}
 
function handleResponse(r: HeadersResponse) {
  // Handle known, and x- prefixed
  const type = r["content-type"]
  const poweredBy = r["x-powered-by"]
  const origin = r.origin // // Unknown keys without the prefix raise errors
}

클래스 타입

인터페이스 구현, 생성자는 constructor:

interface ClockInterface {
  currentTime: Date;
}
 
class Clock implements ClockInterface {
  currentTime: Date = new Date();
  constructor(h: number, m: number) {}
}

setTime 메소드 구현:

interface ClockInterface {
  currentTime: Date;
  setTime(d: Date): void;
}
 
class Clock implements ClockInterface {
  currentTime: Date = new Date();
  setTime(d: Date) {
    this.currentTime = d;
  }
  constructor(h: number, m: number) {}
}

Difference between the static and instance sides of classes:

interface ClockConstructor {
  new (hour: number, minute: number);
}
 
class Clock implements ClockConstructor {
  currentTime: Date;
  constructor(h: number, m: number) {} // new를 구현하지 않았으므로 오류
}
interface ClockConstructor {
  new (hour: number, minute: number): ClockInterface;
}
 
interface ClockInterface {
  tick(): void;
}
 
function createClock(
  ctor: ClockConstructor,
  hour: number,
  minute: number
): ClockInterface {
  return new ctor(hour, minute);
}
 
class DigitalClock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
    console.log("beep beep");
  }
}
 
class AnalogClock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
    console.log("tick tock");
  }
}
 
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
interface ClockConstructor {
  new (hour: number, minute: number): ClockInterface;
}
 
interface ClockInterface {
  tick(): void;
}
 
const Clock: ClockConstructor = class Clock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
    console.log("beep beep");
  }
};
 
let clock = new Clock(12, 17);
clock.tick();

인터페이스 확장:

interface Shape {
  color: string;
}
 
interface Square extends Shape {
  sideLength: number;
}
 
let square = {} as Square;
square.color = "blue";
square.sideLength = 10;

인터페이스 다중 상속:

interface Shape {
  color: string;
}
 
interface PenStroke {
  penWidth: number;
}
 
interface Square extends Shape, PenStroke {
  sideLength: number;
}
 
let square = {} as Square;
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

하이브리드 타입

예시: 함수 타입이면서 객체 타입인 인터페이스:

interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}
 
function getCounter(): Counter {
  let counter = function (start: number) {} as Counter;
  counter.interval = 123;
  counter.reset = function () {};
  return counter;
}
 
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

클래스를 확장한 인터페이스:

class Control {
  private state: any;
}
 
interface SelectableControl extends Control {
  select(): void;
}
 
class Button extends Control implements SelectableControl {
  select() {}
}
 
class TextBox extends Control {
  select() {}
}
 
class ImageControl implements SelectableControl {
  // Class 'ImageControl' incorrectly implements interface 'SelectableControl'.
  // Types have separate declarations of a private property 'state'.
  private state: any;
  select() {}
}

참조