내배캠 66일차

·2023년 1월 18일
0

내일배움캠프

목록 보기
71/142
post-thumbnail

사용한 github주소

TS 기초문법

함수(Function)

웹 애플리케이션을 구현할 때 자주 사용되는 함수는 타입스크립트로 크게 다음 3가지 타입을 정의할 수 있습니다.

  • 함수의 파라미터(매개변수) 타입
  • 함수의 반환 타입
  • 함수의 구조 타입

함수의 기본적인 타입 선언

자바스크립트

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

타입스크립트 - 1

// 암시적 any타입으로 원래는 오류가 안남!
// any type 오류를 나게 하자
// tsconfig.json => "noImplicitAny": true
function sum(a, b) {
  return a + b;
}

타입스크립트 - 2

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

기존 자바스크립트 함수의 선언 방식에서 매개변수 와 함수의 반환 값에 타입을 추가하였습니다.
함수의 반환 값에는 타입을 꼭 추가하지 않아도 되지만, 함수의 반환 값에 타입을 정하지 않을 때는 void라도 사용하는 것이 좋음!

함수의 인자

타입스크립트에서는 함수의 인자를 모두 필수 값으로 간주합니다. 따라서, 함수의 매개변수를 설정하면 undefined나 null이라도 인자로 넘겨야하며 컴파일러에서 정의된 매개변수 값이 넘어 왔는지 확인합니다. 달리 말하면 정의된 매개변수 값만 받을 수 있고 추가로 인자를 받을 수 없다는 의미입니다.

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); // 타입 에러 없음

// 다른 파일에 동일한 이름의 함수가 있어도 오류가 나지않음
export {sum}

매개변수 초기화는 ES6 문법과 동일합니다.

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

console.log({ result1, result3 });

REST 문법이 적용된 매개변수

ES6 문법에서 지원하는 Rest 문법은 타입스크립트에서 다음과 같이 사용할 수 있습니다.

function sum1(a: number, ...nums: number[]): number {
  let totalOfNums = 0;

  for (let key in nums) {
    totalOfNums += nums[key];
  }

  return a + totalOfNums;
}

const result = sum1(10, 20, 30, 40); // 100
console.log(result);

This(arrow function)

타입스크립트에서 자바스크립트의 this가 잘못 사용되었을 때 감지할 수 있습니다.
-this참고-
Understanding Javascript Function Invocation and this
https://velog.io/@padoling/JavaScript-화살표-함수와-this-바인딩

타입스크립트에서 this가 가리키는 것을 명시하려면 아래와 같은 문법을 사용합니다.

interface Node {
  count: number;
  init(this: Node): () => {};
}

let nd: Node = {
  count: 10,
  init: function (this: Node) {
    //화살표함수가 아니고 일반함수라면 undefined가 나옴
    return () => {
      return this.count;
    };
  },
};

let getCount = nd.init();
let count = getCount();

console.log(count); // 10

리터럴 타입(Literal)

리터럴 타입은 집합 타입의 보다 구체적인 하위 타입입니다. 이것이 의미하는 바는 타입 시스템 안에서 "Hello World"는 string이지만, string은 "Hello World"가 아니란 것입니다.

오늘날 TypeScript에는 문자열과 숫자, 두 가지 리터럴 타입이 있는데 이를 사용하면 문자열이나 숫자에 정확한 값을 지정할 수 있습니다.

문자열 리터럴 타입 (String Literal Types)

// @errors: 2345
type Easing = "ease-in" | "ease-out" | "ease-in-out";

class UIElement {
  animate(dx: number, dy: number, easing: Easing) {
    if (easing === "ease-in") {
      // ...
    } else if (easing === "ease-out") {
    } else if (easing === "ease-in-out") {
    } else {
      // 하지만 누군가가 타입을 무시하게 된다면
      // 이곳에 도달하게 될 수 있습니다.
    }
  }
}

let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy");

숫자형 리터럴 타입 (Numeric Literal Types)

function rollDice(): 1 | 2 | 3 | 4 | 5 | 6 {
  // as : 1=>1,2=>2,...,6=>6
  // math.random() : 0~1
  return (Math.floor(Math.random() * 6) + 1) as 1 | 2 | 3 | 4 | 5 | 6;
}

const result4 = rollDice();

유니언과 교차 타입(Union, Intersection)

Union Type

⇒ 여러 타입들을 조합하여 사용하는 방법

function printId(id: number | string) {
  console.log(id.toUpperCase());
  // string | number' 형식에 'toUpperCase' 속성이 없습니다.
  // 'number' 형식에 'toUpperCase' 속성이 없습니다.

  // type narrowing
  // if(typeof id === "string"){
  //   console.log(id.toUpperCase());
  // }
}

=> toUpperCase처럼 어떤 타입에만 존재하는 프로퍼티가 있으면 오류가 발생하기 때문에 type narrowing이 필요함!

Intersection Type

⇒ 유니언 타입은 조합해서 사용한다고 하면 교차 타입은 여러 가지 타입을 결합해서 사용한다고 생각하면 된다.

type Common = {
  name: string,
  age: number,
  gender: string
}

type Animal = {
  howl: string
}

type Cat = Common & Animal;
type Dog = Common | Animal;

let dog: Dog = {
  howl: 'dogggg'
}
let cat: Cat = {
  age: 3,
  gender: 'C',
  name: 'CC',
  howl: 'cattttt'
}

type, interface 차이

type ⇒ interface는 타입과 마찬가지로 객체의 타입의 이름을 지정하는 또 다른 방법입니다.

interface AnimalInterface {
  species: string;
  height: number;
  weight: number;
}

const tiger: AnimalInterface = {
  species: "tiger",
  height: 200,
  weight: 300,
};

type AnimalType = {
  species: string;
  height: number;
  weight: number;
};

const lion: AnimalType = {
  species: "lion",
  height: 180,
  weight: 400,
};

=> 기본적으로는 큰 차이가 없음.
그러나
1. interface는 extends로 확장, type은 &으로!
2. interface에서 할 수 있는 대부분의 기능들은 type에서 가능하지만, 한 가지 중요한 차이점은 type은 새로운 속성을 추가하기 위해서 다시 같은 이름으로 선언할 수 없지만, interface는 항상 선언적 확장이 가능하다는 것입니다.
3. interface는 객체에만 사용이 가능하다.
4. computed value의 사용이 type은 가능하지만 interface는 불가능합니다.

type names = 'firstName' | 'lastName'

type NameTypes = {
  [key in names]: string
}

const yc: NameTypes = { firstName: 'hi', lastName: 'yc' }

interface NameInterface {
  // error
  [key in names]: string
}

+공식문서에서는 특별한 경우를 제외하고는 type보단 interface를 사용하는 것이 더 좋다고 함.

typescript type과 interface의 차이

Class

readonly

클래스의 속성에 readonly 키워드를 사용하면 아래와 같이 접근만 가능합니다.

class Developer {
  readonly name: string;

  constructor(theName: string) {
    this.name = theName;
  }
}

let john = new Developer("John");
john.name = "John"; // error! name is readonly.

Accessor

타입스크립트는 객체의 특정 속성의 접근과 할당에 대해 제어할 수 있습니다. 이를 위해선 해당 객체가 클래스로 생성한 객체여야 합니다. 아래의 간단한 예제를 봅시다.

class Developer {
  name: string;
}

const josh = new Developer();
josh.name = "Josh Bolton";

export { Developer };

위 코드는 클래스로 생성한 객체의 name 속성에 Josh Bolton이라는 값을 대입한 코드입니다. 이제 josh라는 객체의 name 속성은 Josh Bolton이라는 값을 갖겠죠.

여기서 만약 name 속성에 제약 사항을 추가하고 싶다면 아래와 같이 get과 set을 활용합니다.

class Developer {
  private _name: string;

  get name(): string {
    return this._name;
  }

  set name(newValue: string) {
    if (newValue && newValue.length > 5) {
      throw new Error("이름이 너무 깁니다");
    }
    this._name = newValue;
  }
}

const josh = new Developer();
josh.name = "Josh";
// josh._name = "Josh"; //private라서 바로접근불가
console.log(josh.name); //Josh

export { Developer };

=> console.log(josh.name) 할 때 get사용, josh.name = "Josh" 할 때 set사용
=> get만 선언하고 set을 선언하지 않는 경우에는 자동으로 readonly로 인식됩니다.

TypeScript - 클래스, 접근제한자

Abstract Class

추상 클래스(Abstract Class)는 인터페이스와 비슷한 역할을 하면서도 조금 다른 특징을 갖고 있습니다. 추상 클래스는 특정 클래스의 상속 대상이 되는 클래스이며 좀 더 상위 레벨에서 속성, 메서드의 모양을 정의합니다.

abstract class Developer {
  abstract coding(): void; // 'abstract'가 붙으면 상속 받은 클래스에서 무조건 구현해야 함
  drink(): void {
    console.log("drink sth");
  }
}

class FrontEndDeveloper extends Developer {
  coding(): void {
    // Developer 클래스를 상속 받은 클래스에서 무조건 정의해야 하는 메서드
    console.log("develop front");
  }
  design(): void {
    console.log("design front");
  }
}

class BackEndDeveloper extends Developer {
  coding(): void {
    // Developer 클래스를 상속 받은 클래스에서 무조건 정의해야 하는 메서드
    console.log("develop server");
  }
  design(): void {
    console.log("design server");
  }
}

// const dev = new Developer(); // error: cannot create an instance of an abstract class
const josh = new BackEndDeveloper();
const kai = new FrontEndDeveloper();

josh.coding(); // develop server
josh.drink(); // drink sth
josh.design(); // design server

console.log("");

kai.coding(); // develop front
kai.drink(); // drink sth
kai.design(); // design front

export { Developer };
  • 추상클래스 - 추상 클래스를 정의할 때는 class 앞에 abstract라고 표기합니다. 또한 추상 메서드를 정의할 때도 abstract를 메서드 이름 앞에 붙입니다. 추상 메소드는 정의만 있을 뿐 몸체(body)가 구현되어 있지 않습니다. 몸체는 추상 클래스를 상속하는 클래스에서 해당 추상 메소드를 통해 필히 구현해야 합니다.그리고 추상 클래스는 추상 메서드 뿐만 아니라, 실 사용이 가능한 메서드도 정의할 수 있습니다. 추상 클래스를 상속하는 클래스를 통해 생성된 인스턴스는 이 메서드를 사용할 수 있습니다. 추상 클래스는 말 그대로 추상이므로 클래스와 달리 인스턴스를 생성하지 않습니다. 생성 구문을 사용하면 오류가 발생합니다.
  • **추상 클래스(Abstract) 용도 완벽 이해하기 -** https://inpa.tistory.com/entry/JAVA-☕-추상-클래스Abstract-용도-완벽-이해하기
  • 메소드 오버라이딩 - http://www.tcpschool.com/java/java_inheritance_overriding
profile
개발자 꿈나무

0개의 댓글