📍 23일 차 11.25. 목(실시간 강의)
오늘은 interface
, generic
문법에 대해서 배웠다.
❏ Interface
- 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용 가능
- 직접 인스턴스를 생성할 수 없음. 모든 메서드는 추상 메서드다. (이때
abstract
키워드를 사용하지 않는다)
- 선언만 존재한다. (JS로 변환되면 인터페이서는 사라진다.)
interface
간에 extends
를 사용하여 다중 상속 가능(class와 비슷)
- 추상 클래스와 다른 점: 추상
class
는 전체적인 구조, interface
는 프로그래머 간의 협업 개발을 할 때 사용
- 함수의 매개변수(파라미터로)로 사용
API
응답에서 데이터의 구조를 결정할 때 사용
| |
| interface User { |
| [grade: number]: "A" | "B" | "C" | "D" |
| } |
| |
| let user: User = { |
| 1: "A", |
| 2: "B", |
| 5: "D", |
| } |
- 선택적 프로퍼티(?):
?
가 붙는 경우는 반드시 구현되지 않고 선택 가능
| |
| interface User { |
| 1: string; |
| 2?: string; |
| 5: string |
| } |
| |
| let user: User = { |
| 1: "A", |
| 5: "D", |
| } |
- 클래스 선언문의
implements
를 붙여서 사용 가능(인터페이스를 구현하는 클래스의 일관성을 유지할 수 있는 장점을 가짐)
| |
| |
| interface ITodo { |
| id: number; |
| content: string; |
| completed: boolean; |
| sayHello(): void; |
| } |
| |
| class Todo implements ITodo { |
| constructor( |
| public id: number, |
| public content: string, |
| public completed: boolean |
| ){} |
| |
| sayHello(){ |
| console.log(`안녕하세요!!`) |
| } |
| } |
| |
| const today = new Todo(1, "TED", true); |
| console.log(today) |
| today.sayHello(); |
- 함수와 변수에서도 덕타입이 적용된다.(덕 타이핑: 타입 체크에서 중요한 것은 값을 실제 가지고 있느냐에 관한 것, 인터페이스에서 정의한 프로퍼티나 메서드를 가지고 있다면 그 인터페이스를 구현한 것으로 여김 덕 타이핑 또는 구조적 타이핑이라 부름)
| type IPerson = { |
| name: string |
| } |
| |
| function sayHello(person: IPerson): void{ |
| console.log(`Hello ${person.name}`) |
| } |
| |
| const me = {name : "TED", age: 27}; |
| sayHello(me); |
❏ 디자인패턴
- 생성패턴: 객체를 생성하는데 관련한 패턴
- 구조패턴: 프로그램의 구조에 관련한 패턴
- 행위패턴: 반복적으로 사용되는 객체들의 상호작용 패턴
| |
| interface Weapon { |
| attack: () => void; |
| } |
| |
| class Sword implements Weapon { |
| public attack() { |
| console.log("검 공격"); |
| } |
| } |
| |
| class Spear implements Weapon { |
| public attack() { |
| console.log("창 공격"); |
| } |
| } |
| |
| class Bow implements Weapon { |
| public attack() { |
| console.log("활 공격"); |
| } |
| } |
| |
| class GameUser { |
| private weapon: Weapon | null = null; |
| |
| public setWeapon(weapon: Weapon) { |
| this.weapon = weapon; |
| } |
| |
| public attack() { |
| if (this.weapon === null) { |
| console.log("맨손 공격"); |
| } else { |
| this.weapon.attack(); |
| } |
| } |
| } |
| |
| const testUser = new GameUser(); |
| testUser.setWeapon(new Spear()); |
| testUser.attack(); |
| |
| testUser.setWeapon(new Bow()); |
| testUser.attack(); |
| |
| testUser.setWeapon(new Sword()); |
| testUser.attack(); |
| |
| testUser.setWeapon(null); |
| testUser.attack(); |
❏ 제네릭(Generic)
- 데이터 타입을 일반화함
- 자료형을 정하지 않고 여러 타입을 사용할 수 있게 한다.(범용적인 함수를 쓰고 싶을 때?, 여러 가지 타입을 사용하고 싶을 때)
- 재사용성이 높은 함수를 만들어줄 때 사용됨
- 한 가지 타입보다 여러 가지 타입에서 동작하는 함수를 생성한다.
| |
| class Stack<T> { |
| private data: T[] = []; |
| |
| constructor(){} |
| |
| push(item: T): void { |
| this.data.push(item); |
| } |
| |
| pop():T | undefined{ |
| return this.data.pop() |
| } |
| } |
| |
| |
| const numberStack = new Stack<number>(); |
| const stringStack = new Stack<string>(); |
| numberStack.push(1); |
| stringStack.push('a') |
| |
| |
| function getSize<T>(arr: T[]): number { |
| return arr.length; |
| } |
| |
| const arr1 = [1, 2, 3]; |
| getSize<number>(arr1); |
| |
| const arr2 = ['a', 'b', 'c']; |
| getSize<string>(arr2); |
- 제네릭 한 변수를 추가하고 싶을 때
<T, U>
| |
| function toPair<T, U>(a: T, b: U): [T, U]{ |
| return [a, b]; |
| } |
| |
| toPair<number, string>(1, "1"); |
| toPair<string, boolean>("Hello", true); |
| |
| |
| function logText<T>(text: T[]): number{ |
| return text.length; |
| } |
| |
| |
| interface Mobile<T>{ |
| name: string; |
| price: number; |
| option: T; |
| } |
| |
| let myInfo: Mobile<boolean> = { |
| name: "TED", |
| price: 6000, |
| option: true |
| } |
| |
| let yourInfo: Mobile<object> = { |
| name: "TED", |
| price: 6000, |
| option: { |
| age: 28, |
| color: "orange", |
| } |
| } |
| |
| |
| |
| interface GenericLogTextFn { |
| <T>(text: T): T; |
| } |
| |
| interface GenericLogTextFn<T> { |
| (text: T): T |
| } |
| |
| function logText<T>(text: T): T{ |
| return text; |
| } |
| |
| let myString: GenericLogTextFn<string> = logText; |
| console.log(myString) |
| |
| interface Mobile<T> { |
| name: string; |
| price: number; |
| option: T; |
| } |
| |
| interface OptionType { |
| color: string; |
| coupon: boolean; |
| } |
| |
| const m1: Mobile<OptionType> = { |
| name: "s21", |
| price: 1000, |
| option: { color: "red", coupon: false }, |
| }; |
| |
| |
| interface User { |
| name: string; |
| age: number; |
| } |
| |
| interface Book { |
| price: number; |
| } |
| |
| let user: User = { name: "a", age : 10 }; |
| let book: Book = { price: 3000 }; |
| |
| function showName<T extends { name : string }>(data: T){ |
| return data.name; |
| } |
❏ Non-null assertion operator
Null
이 아닌 어선별 연산자는 피연산자가 null
이 아니라고 컴파일러에게 전달하여 일시적으로 Null
제약조건을 완화합니다. 권장하지 않는 문법이고 대신 &&
을 사용한다.
❏ Union type
| |
| function getAge(age: number | string){ |
| return typeof age === "number" ? age.toFixed() : age; |
| } |
| |
| const myAge = getAge(28); |
| console.log(myAge) |
| |
| const yourAge = getAge("25"); |
| console.log(yourAge) |
| |
| |
| interface Person { |
| name: string; |
| age: number; |
| } |
| |
| interface Developer { |
| name: string; |
| skill: string; |
| } |
| |
| |
| function introduce(someone: Person | Developer){ |
| someone.name; |
| someone.age; |
| someone.skill; |
| } |
| |
| |
| function introduce(someone: Person & Developer){ |
| someone.name; |
| someone.age; |
| someone.skill; |
| } |
❏ 제약 조건(constraints / keyof)
- 제네릭 타입에 어느 정도 힌트를 주고 제약을 걸기 위해 사용, 최소한
length
속성이 있어야 에러가 나지 않는다
| interface LengthWise { |
| length: number; |
| } |
| |
| interface Rect { |
| length: number; |
| area: number; |
| } |
| |
| function logText1<T extends LengthWise | Rect>(text: T){ |
| console.log(text.length); |
| console.log(text.area); |
| } |
| |
| function logText1<T extends LengthWise & Rect>(text: T){ |
| console.log(text.length); |
| console.log(text.area); |
❏ keyof를 사용한 제약
- 객체의 속성을 제약하고 싶을 때 사용
| |
| function getProperty<T, O extends keyof T>(obj: T, key: O){ |
| return obj[key] |
| } |
| |
| let obj = { a: 1, b: 2, c: 3 } |
| |
| console.log(getProperty(obj, 'c')); |
❏ 기타 기능 및 개념
- Type Assertion: 타입 추론기능은 강력하지만 한계 존재, 타입단언(type assertion)은 타입스크립트가 추정하지 못하는 부분까지 개발자가 선언하는 것
- as
| let a; |
| a = 10; |
| a = 'abc'; |
| let b = a; |
| |
| b = a as string; |
| |
| interface Person{ |
| name: string; |
| age: number; |
| } |
| |
| interface Developer{ |
| name: string; |
| skill: string; |
| } |
| |
| function introduce(someone: Person | Developer){ |
| someone.name; |
| (someone as Person).age; |
| (someone as Developer).skill; |
| } |
- typeGuard
| |
| function doSomeThing(id: string | number){ |
| if (typeof id === 'string'){ |
| console.log(id.trim()); |
| }else { |
| console.log(id); |
| } |
| } |
| |
| |
| class Diner {} |
| class Merchant {} |
| |
| function doSomething(user: Diner | Merchant){ |
| if(user instanceof Diner){ |
| }else{ |
| } |
| } |
| |
| |
| interface Bird { |
| fly(): string; |
| } |
| |
| interface Fish { |
| swim(): number; |
| } |
| |
| function doSomething(animal: Fish | Bird){ |
| if('swim' in animal){ |
| console.log((animal as Fish).swim()) |
| }else{ |
| console.log((animal as Bird).fly()) |
| } |
| } |
| |
| doSomething({ |
| swim: () => { |
| return 82 |
| } |
| }) |
| |
| doSomething({ |
| fly: () => { |
| return "Hello" |
| } |
| }) |
| |
| |
| function isFish(animal: Fish | Bird): animal is Fish { |
| return (animal as Fish).swim !== undefined; |
| } |
| |
| function doSomething(animal: Fish | Bird){ |
| if (isFish(animal)) { |
| animal.swim() |
| }else{ |
| animal.fly() |
| } |
| } |
❏ 실습
- 클래스 vs 추상클래스: 어떤 메서드는 그냥 구현해도 되고, 어떤 메서드는 정의되지 않은 메서드 일 수 있다.(사용하려면 상속이 필요하다)
- 인터페이스: 추상클래스와 달리
abstract
를 사용하지 않음. 구성되는 것의 타입만 명시해서(생성자나 메서드 구현을 작성할 필요가 없다.) (하나의 interface에 여러 데이터가 뭉치지 않도록 분산시킬 수 있기 때문에 다중 상속이 가능하다.)
interface
vs type
vs abstract
| |
| interface Test { |
| name: string; |
| id: number; |
| introduce(): void; |
| } |
| |
| |
| type Test { |
| name: string; |
| id: number; |
| introduce(): void; |
| } |
| |
| |
| abstract class Test { |
| name: string; |
| id: number; |
| abstractintroduce(): void; |
| } |
T extends number | string
: 2개 이상 상속이 가능함.
interface
활용 예시: 데이터베이스 조작(로그인, 게시물, 등등)
- ORM: SQL문을 대신해서 내가 알고 있는 프로그래밍 언어로 DB로 조작하는 방법(나중에 SQL문으로 변환된다)
❏ Generic
- 선언한 시점에 타입을 사용하는 것이 아니라 사용할 때 모든 타입을 마음대로 넣을 수 있다.
- 타입에 제한을 걸기 위해
<T extends string | number>
처럼 사용할 수 있다.
❏ 자바스크립트의 동적인 타입 예시
true == "true"
: false
abstract equality composition (피연산자에 number
씌우고 비교한다)
010-03=5
, 0 붙어서 8진수가 된다. (16진수는 ox, 2진수는 ob)
1+'1' = '11'
(+
는 문자열 우선순위), 11-'1' = 10
(-
는 Number
만 지원한다.)
0/0=NaN
, 숫자로 취급하지 않는다.
1 / 0 > 10 * 1000 = true
: True, infinity는 표현 범위를 넘어서면 취급한다
true++=>Error
: 값에다가 붙이는 게 아니라 변수에다 선언해야 한다.
1+2+"3" = 33
댓글