[엘리스] 5주차-2 복습 정리

Hyejis·2023년 1월 30일
0

엘리스

목록 보기
7/11

TypeScript와 Class

0. 타입스크립트를 쓰는 이유

자바스크립트의 특징인 동적 타이핑을 이용하기 위해
런타임 단계에서 타입 에러를 확인하고 고칠 수 있다.
컴파일 단계에서 타입 에러를 미리 확인할 수 있습니다.
타입을 하나하나 명시해야 하기 때문에 개발 속도는 오래 걸린다.
타입스크립트를 이용하면 유지 보수가 유리하기 때문에 개발 속도도 자연스럽게 빨라진다.

1. 타입스크립트

1) 타입스크립트

마이크로소프트에서 자바스크립트의 한계를 해결하기 위해 개발한 상위 집합 오픈 소스 언어
자바스크립트 -> 타입스크립트 : 컴파일러 필요x
타입스크립트 -> 자바스크립트 : 컴파일러 필요o

타입스크립트를 쓰는 이유

  1. 동적 타입을 정적으로 선언 가능
  2. 타입 유추를 통한 타입 제어 가능
  3. 컴파일 시점 오류 포착 가능
  4. JS에서 찾을 수 없는 추가 코드 기능 제공

타입 유추를 통한 타입 제어

타입을 미리 지정해두어 각 타입에 맞는 변수만을 입력할 수 있도록 한다.

const sum = (a: number, b: numder) => {
  return a+b
}
sum(1,2)	// 3
--------------------------------------------
만약 b에 string 값을 할당할 시,
  // type 'string' is not assignable to type 'number' .ts(2322)
가 출력된다.

2) 타입스크립트의 기본 Type

TS는 JS코드에 변수나 함수 등 type을 정의
Type을 나타내기 위해 타입표기(type annotation)
TS의 Type: 기본자료형(primitive type), 참조 자료형(reference type), 추가 제공 자료형

1. 기본 자료형(primitive type)

object와 reference 형태가 아닌 실제 값을 저장하는 자료형
primitive type 내장 함수를 사용 가능한 것은 자바스크립트의 처리 방식 덕분
종류: string, boolean, number, null, undefined, symbol(ES6 추가)

  • string, boolean, number
let str: string = "hi";	// string: 문자열 저장
  • null, undefined
let n: null = null;	// null: 값이 의도적으로 비어 있는 상태를 저장
let u: undefined = undefined;	// undefined: 아무 값이 할당되지 않은 상태를 저장
// typeof로 데이터 타입 확인
typeof null;	// 'object'
typeof undefined;	// 'undefined'

null===undefined	// false
null==undefined		// true
null===null			// true
null==null			// true
!null				// true
isNan(1+null)		//false
isNan(1+undefined)	// true

2. 참조 자료형(reference type)

객체, 배열, 함수 등과 같은 object 형식의 타입
메모리에 값을 주소로 저장하고 출력 시 메모리 주소와 일치하는 값을 출력
종류: object(기본 자료형에 해당하지 않는 타입), array, function

function create(o: object): void{}
create({prop: 0})	// 성공
create([1,2,3])		// 성공
// create()에 string, boolean, number, null, undefined를 할당하면 error

let arr: number[]=[1,2,3]		// array: 배열을 저장하는 타입
let arr: Array<number>=[1,2,3]	// generic을 사용한 표기 가능

제공 자료형

추가 제공 자료형: ts에서 개발자의 편의를 위해 추가로 제공하는 타입
종류: tuple, enum, any, void, never

  • tuple: 길이와 각 요소의 타입이 정해진 배열을 저장하는 타입
let arr: [string,number]=["Hi",6];	
arr[1].concat("!");	// error, 'number' does not have 'concat'
arr[3]="hello";		// 정의하지 않은 index 호출 시 오류
// error,property '3' does not exist on type '[string,number]'
  • enum: 특정 값(상수)들의 집합을 저장하는 타입
enum Car {BUS, TAXI, SUV};	
let bus: Car = Car.BUS;
let bus: Car = Car[0];	// 인덱스 번호로 접근
enum Car { BUS = 1, TAXI = 2, SUV = 3};	// 인덱스를 사용자 임의로 변경
let taxi: String = Car[2];
enum Car {BUS = 2, TAXI, SUV};
let taxi: String = Car[3];
  • any: 모든 타입 저장 가능, 컴파일 중 타입 검사 안함
let str: any = "hi";	
let num: any = 10;
let arr: any = ["a", 2, true];
  • void: 함수에서 반환 값 없을 시, any의 반대 타입
let unknown: void = undefined;	
function sayHi(): void{			// 변수에는 undefined와 null만 할당하고,
  console.log("hi");			// 함수에는 반환 값을 설정할 수 없는 타입
}
  • never: 발생할 수 없는 타입
function neverEnd(): never{		
  while(true){}				// 항상 오류를 발생시키거나 절대 반환하지 않는 반환 타입
}							// 종료되지 않는 함수
  • error: a function returning 'never'
function neverEnd(): never{		
  while(true){				// cannot have a reachable end point.ts(2534)
    break;
  }
}
  • 항상 오류를 발생시키는 함수
function error(message: string): never{	
  throw new Error(message);
}

3) Utility types

TS는 공통 타입 변환을 용이하게 하기 위해 유틸리티 타입을 제공
유틸리티 타입은 전역으로 사용 가능

종류

Patial<T>, Readonly<T>
Record<T> ,Pick<T,K>
Omit<T,K>, Exclude<T,U>, Extract<T,U>
NonNullable<T>, Parameters<T>,ConstructorParameters<T>
Return Type<T>, Required<T>

Patial<T>

프로퍼티를 선택적으로 만드는 타입을 구성
주어진 타입의 모든 하위 타입 집합을 나타내는 타입을 반환

interface Todo{	// Todo 타입 지정
  title: string;
  description: string;
  }
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>){
  return{...todo,...fieldsToUpdate};
}
const todo1 = {
  title: '멋진 하루 보내기',
  description: '끝장나게 숨쉬기',
};
const todo2 = updateTodo(todo1, {
  description: '집청소하기',
});

Readonly<T>

프로퍼티를 읽기 전용으로 설정한 타입을 구성

interface Todo{
  title: string;
}
const todo: Readonly<Todo> = {
  title: '읽기만 가능합니다',
};
todo.title = '쓰고싶어요';
// error: cannot as sign to 'title' because it is a read-only property

Record<T>

프로퍼티의 집합 K로 타입을 구성.
타입의 프로퍼티들을 다른 타입에 매핑

interface PageInfo{
  title: string;
}
type Page = 'home' | 'about' | 'contact';

const x: Record<Page, PageInfo> = {
  about: {title: 'about'},
  contact: {title: 'contact'},
  home: {subTitle: 'home'},	// error: '{subTitle: string;}' is not assignable
  main:{title: 'home'},	// error: main is not assignable to type 'Page'
};

Pick<T,K>

프로퍼티 K의 집합을 선택해 타입을 구성

interface Todo{
  title: string;
  description: string;
  completed: boolean;
}
type TodoPreview = Pick<Todo, 'title' | 'completed'>;

const todo: TodoPreview = {
  title: '취업',
  completed: false,
  description: 'description'	
  // error: 'description' is not assignable to type
}

Omit<T,K>

모든 프로퍼티를 선택한 다음 K를 제거한 타입을 구성

interface Todo{
  title: string;
  description: string;
  completed: boolean;
}
type TodoPreview = Omit<Todo, 'description'>;

const todo: TodoPreview = {
  title: 'Clean room',
  completed: false,
  description: 'description'	
  // error: 'description' is not assignable to type
}

Exclude<T,U>

T에서 U에 할당할 수 있는 모든 속성을 제외한 타입을 구성

type T0 = Exclude<"a"|"b"|"c", "a">;	// "b"|"c"
type T1 = Exclude<"a"|"b"|"c", "a"|"b">;	// "c"
type T2 = Exclude<string|number|(() => void), Function>;	// string|number

Extract<T,U>

T에서 U에 할당할 수 있는 모든 속성을 추출하여 타입을 구성

type T0 = Extract<"a"|"b"|"c", "a"|"f">;	// "a"
type T1 = Extract<string|number|(() => void), Function>;	// () => void

NonNullable<T>

null과 undefined를 제외한 타입

type T0 = NonNullable<string|number|undefined>;	// string|number
type T1 = NonNullable<string[]|null|undefined>;	// string[]

Parameters<T>

함수 타입 T의 매개 변수 타입들의 튜플 타입을 구성

declare function f1(arg: {a: number, b: string}): void
type T0 = Parameters<() => string>;			// []
type T1 = Parameters<(s: string) => void>;	// [string]
type T2 = Parameters<(<T>(arg: T) => T)>;	// [unknown]
type T3 = Parameters<typeof f1>;	// [{a: number, b: string}]
type T4 = Parameters<any>;			// unknown[]
type T5 = Parameters<never>;		// never
type T6 = Parameters<string>;		// 오류
type T7 = Parameters<Function>;		// 오류

ConstructorParameters<T>

생성자 함수 타입의 모든 매개 변수 타입을 추출
모든 매개 변수 타입을 가지는 튜플 타입(T가 함수가 아닌 경우 never)를 생성

type T10 = ConstructorParameters<ErrorConstructor>;	// [(string | undefined)?]
type T1 = ConstructorParameters<FunctionConstructor>;	// string[]
type T2 = ConstructorParameters<RegExpConstructor>;	
// [string, (string | undefined)?]

interface I1{
  new(args: string): Function;
}
type T12 = ConstructorParameters<I1>;	// [string]

function f1(a: T12){
  a[0]
  a[1]	// error: tuple type '[args: string]' of length '1' has no element at index '1'.
}

Return Type<T>

함수 T의 반환 타입으로 구성된 타입을 생성

declare function f1(): {a: number, b: string}
type T0 = ReturnType<() => string>;		// string
type T1 = ReturnType<(s: string) => void>;	// void
type T2 = ReturnType<(<T>() => T)>;		// {}
type T3 = ReturnType<(<T extends U, U extends number[]>() => T)>;	// number[]
type T4 = ReturnType<typeof f1>;	// {a: number, b: string}
type T5 = ReturnType<any>;		// any
type T6 = ReturnType<never>;	// any
type T7 = ReturnType<string>;	// 오류
type T8 = ReturnType<Function>;	// 오류

Required<T>

T의 모든 프로퍼티가 필수로 설정된 타입을 구성

interface Props{
  a?: number;
  b?: string
};
const obj: Props = {a:5};
const obj2: Requires<Props> = {a:5};
// Error: property 'b' is missing in type '{a: number;}'

4) 타입스크립트를 이용해 함수 사용하기

매개변수: 함수를 정의할 때 사용되는 값
인수: 함수를 호출할 때 사용되는 값
인자값 == 매개변수 == Parameter

일급객체

일급객체: 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체
JS와 TS의 함수 == 일급객체

  • 일급객체의 조건
    다른 함수에 매개변수로 제공 가능, 함수에서 반환 가능, 변수에 할당 가능

함수 선언 방법

  • 함수 선언식
function hello(name){
  return `hello ${name}`;
}
  • 함수 표현식
let hello = function(name){
  return `hello ${name}`;
}
  • 화살표 함수 표현식
let hello = (name) =>{
    return `hello ${name}`;
}
  • 단축형 화살표 함수 표현식
let hello = (name) => `hello ${name}`;
  • 함수 생성자
let hello = new Function("name", 'return"hello"+name');
// 함수 생성자는 되도록 사용 권장x

TS 함수 작성 시 반환 타입을 추론하도록 작성하는 걸 권장
함수의 매개 변수와 인수의 타입이 호환 가능하게 작성
인수의 타입을 잘못 전달하면 에러 발생

타입 추론

TS 컴파일러는 방정식의 한쪽에만 타입이 있더라도 타입 추론 가능 = contextual typing
=> 프로그램에서 타입을 유지하기 위한 노력을 줄일 수 있다.

// 일반적인 함수
let f12 = function (x: number, y: number): number{
  return x+y;
}
// contextual typing
let f12: (baseValue: number, increment: number) => number = function (x,y){
  return x+y;
}

5) 함수의 매개변수

기본 매개변수

함수에 주어진 인자의 수는 함수가 기대하는 매개변수의 수와 일치해야 한다.

function buildName(firstName:string, lastName: string){
  return firstName + " " + lastName;
}
let result = buildName("Kim", "Bero");	

선택적 매개변수

js에서는 모든 매개변수가 선택적으로, 인수가 없다면 undefined가 된다.
ts에서도 변수명 뒤에 ? 를 붙여 선택적 매개변수 사용 가능

function buildName(firstName:string, lastName?: string){
  if(lastName) return firstName + " " + lastName;
  else return firstName;
}
let result1 = buildName("Kim");
let result2 = buildName("Kim", "Bero");

기본 - 초기화 매개변수

ts에서는 값을 제공하지 않거나, undefined로 했을 때에 매개변수의 값 할당 가능

function buildName(firstName:string, lastName: "Beri"){
  return firstName + " " + lastName;
}
let result1 = buildName("Kim");		// "Kim Beri"
let result2 = buildName("Kim", undefined);	// "Kim Beri"
let result3 = buildName("Kim", "Bero");	// "Kim Bero"

나머지 매개변수

컴파일러는 생략부호 (...) 뒤의 인자 배열을 빌드해 함수에서 사용
나머지 매개변수는 매개변수의 수를 무한으로 취급
아무것도 넘겨주지 않을 수도 있다.

function buildName(firstName:string, ...restOfName: string[]){
  return firstName + " " + restOfName.join(" ");	
}
let hamsterName = buildName("Bero", "Beri", "Bebe", "Cream");
// "Bero Beri Bebe Cream"

기본-초기화 매개변수(Default Parameter)

타입스크립트에서는 값을 제공하지 않거나, undefined로 설정한 매개변수의 값에 기본 값을 할당할 수 있습니다.

2. Class

1) 객체 지향 프로그래밍(OOP)

OOP는 컴퓨터 프로그램을 객체의 모임으로 파악하려는 프로그래밍 패러다임
객체들은 서로 메세지를 주고 받을 수 있으며 데이터를 처리할 수 있다.

OOP의 장점

프로그램을 유연하고 변경이 용이하게, 개발과 보수를 간편하게, 직관적인 코드 분석 가능
객체 지향 프로그래밍의 중요한 특성: 강한 응집력과 약한 결합력 지향

Class

객체를 정의하는 틀 또는 설계도와 같은 의미로 사용
클래스의 요소: 멤버(필드, 생성자, 메소드)
인스턴스: new 연산자에 의해 생성된 객체

  • Class 생성
    new를 사용해 Person 클래스의 인스턴스를 생성
    Person class의 멤버는 name, constructor, say()가 있다.
    클래스 안에서 "this."를 앞에 붙이면 클래스의 멤버를 의미
class Person{
  name: string;
  constructor(name:string){
    this.name = name;
  }
  say(){
    return "Hello, My name is" + this.name;
  }
}
let person = new Person("Bibi");

2) 접근 제어자/상속

접근 제어자

속성 또는 메소드로의 접근을 제한하기 위해 사용
ts에는 public > protected > private 3종류의 접근 제어자가 존재
java와 다르게 package 개념이 없어 default 접근 제어자는 존재하지 않는다.

  • public
    프로그램 내에서 선언된 멤버들이 자유롭게 접근 가능
    ts에서 멤버는 기본적으로 public으로 선언
    명시적으로 멤버를 public으로 표시 가능
class Animal {
  public name: string
  constructor(theName: string){
    this.name = theName;
  }
}
new Animal("Cat").name;
  • private
    멤버가 포함된 클래스 외부에서의 접근을 막는다.
class Animal {
  private name: string
  constructor(theName: string){
    this.name = theName;
  }
}
new Animal("Cat").name;
// error: property 'name' is private and only accessible within class 'Animal'

protected

멤버가 포함된 클래스와 그 하위 클래스 외부에서의 접근을 막는다.
Person에서 파생된 Employee의 인스턴스 메소드에서는 name을 사용할 수 있다.

class Person{
  protected name: string;
  constructor(name:string){
    this.name = name;
  }
}
class Employee extends Person{
  private department: string
  constructor(name: string, department: string){
    super(name);
    this.department = department;
  }
  public getElevatorPitch(){
    return `Hello, my name is ${this.name} and I work in ${this.department}.`;
  }
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name);	// error

상속

OOP는 상속을 이용하여 존재하는 클래스를 확장해 새로운 클래스를 만들 수 있다.
파생된 클래스는 하위클래스(subclass), 기초 클래스는 상위클래스(superclass)

class Animal {
  move(distanceInMeters: number){
    console.log(`Animal moved ${distanceInMeters}m.`);
  }
}
class Dog extends Animal{	
  // extends 키워드로 Animal이라는 기초 클래스에서 Dog 클래스가 파생
  makeSound(){
    console.log("멍멍!");
  }
}
class Cat extends Animal{
  makeSound(){
    console.log("야옹!");
  }
}
const dog = new Dog();
dog.move(10);
dog.makeSound();
const cat = new Cat();
cat.move(5);
cat.makeSound();

Dog, Cat은 Animal의 기능(move 메소드)을 확장하기 때문에 move()와 makeSound()를 가진 인스턴스를 생성

3) Getters & Setters / readonly / static

Gettes & Setters

비공개로 설정하려는 속성은 private로 설정하고 속성값을 읽고 수정하는 getter/setter 함수를 사용

  • class의 속성에 직접 접근하는 것을 막고 getter, setter 함수를 사용해 값을 받아오거나 수정
  • 속성에 직접 접근해 수정하면 데이터 무결성이 깨질 수 있어 캡슐화를 권장한다.
  • 각 객체의 멤버에 접근하는 방법을 세밀하게 제어
class Person{
  private _name: string
  get name(){
    return this._name;
  }
  set name(name: string){
    if(name.length > 10){
      throw new Error("name too long")
    }
    this._name = name;
  }
}
let person = new Person();
console.log(person.name);	// undefined
person.name = "june";
console.log(person.name)	// june
person.name = "junejunejunejune";	// throw error

readonly

읽기만 가능한 속성을 선언

  • 속성을 읽기 전용으로 설정해 변경할 수 없게 만든다.
  • 선언될 때나 생성자에서 값을 설정하면 이후 수정할 수 없다.
class Person{
  readonly age: number = 20	// 선언 초기화
  constructor(age: number){
    this.age = age;
  }
}
let person = new Person(10);	// 생성자 초기화
person.age = 30;
// error: cannot assign to 'age' because it is a read-only property

static

전역멤버(객체마다 할당되지 않고 클래스의 모든 객체가 공유하는 멤버)를 선언

  • 각 인스턴스가 아닌 클래스 자체에서 보이는 전역 멤버를 생성 => 범용적으로 사용되는 값에 설정
  • "클래스명."을 앞에 붙여 static 멤버에 접근 가능
  • ES6에서는 메서드 전용 속성에는 선언이 안되었으나 TS에서는 사용 가능
class Grid{
  static origin = {x: 0, y: 0}
  calculateDistanceFromOrigin(point: {x: number; y: number}){
    let xDist = point.x - Grid.origin.x;
    let yDist = point.y - Grid.origin.y;
    return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
  }
  constructor(public scale: number){}
}
let grid1 = new Grid(1.0)	// 1xscale
let grid2 = new Grid(5.0)	// 5xscale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

④ static
static은 각 인스턴스가 아닌 클래스 자체에서 보이는 전역 멤버를 생성합니다. 그래서 범용적으로 사용되는 값에 static을 설정하여 사용합니다.

4) 추상클래스

다른 클래스들이 파생될 수 있는 기초 클래스로, 직접 인스턴스화 불가능
abstract 키워드: 추상 클래스나 추상 메소드를 정의하는 데 사용
추상 메소드는 클래스에는 구현되어 있지 않고 파생된 클래스에서 구현해야 한다.

abstract class Animal{
  protected name: string
  constructor(name: string){
    this.name = name;
  }
  abstract makeSound(): void
  move(): void{
    console.log("move!!");
	}
}
class Dog extends Animal {
  constructor(name: string) {
    super(name);		// 파생된 클래스의 생성자는 반드시 super()를 호출
  }
  makeSound(): void {	// 반드시 파생된 클래스에서 구현 필수
    console.log(this.name + " 멍멍!!")
  }
}
const animal = new Animal("animal");
// error: cannot create an instance of an abstract class
const dog = new Dog("진돗개")
dog.makeSound() // 진돗개 멍멍!!

추상 클래스를 활용한 디자인 패턴(Template Method Pattern)

프로그램의 일부분을 서브 클래스로 캡슐화해 특정 단계의 기능을 바꾸는 것
전체적인 알고리즘은 상위 클래스에서 구현, 다른 부분은 하위 클래스에서 구현
코드 중복을 최소화할 수 있다.

abstract class Parent{	// 템플릿 메소드: 자식에서 공통적으로 사용하는 부분
  public do(){
    console.log("Parent에서 실행 - 상");
    this.hook();	// 훅 메소드: Child에서 구현해야 할 부분
    console.log("Parent에서 실행 - 하");
  }
  abstract hook(): void
}
class Child extends Parent{
  hook(): void{
    console.log("Child");
}
const child = new Child();
child.do();
// 실행 결과
// Parent에서 실행 - 상
// Child
// Parent에서 실행 - 하
profile
프론트를 시작으로 풀스택을 걸쳐 모든 분야를 아우르고 싶은 야망이 살짝 있는 아기 개발자입니다.

0개의 댓글