프론트엔드 데브코스 5기 TIL 33 part2 - TS(2) 인터페이스, 함수, 접근제어자, 추상클래스, 데코레이터

김영현·2023년 11월 10일
0

TIL

목록 보기
40/129

인터페이스

type UserA = {
	name: string
  	age: number
}
interface UserB {
	name: string
  	age: number
  	isValid?: boolean
  	readonly val: string
}
const user: UserB{
	name: 'kim',
    age: 85
}
user.val = 5 // 에러

흠! 여러 타입을 선언한것과 뭐가 다를까?
찾아보니 interface는 선언 병합이 가능하다고 한다. 같은이름을 선언하면 컴파일 시점에 둘이 합쳐짐.
그래서 interface를 조금 더 권장한다고한다.

  • ?키워드 : 해당 프로퍼티가 있어도 없어도 됨
  • readonly키워드 : 해당 프로퍼티 재할당 불가.

인터페이스의 함수, 클래스 타입

interface User{
	name:string
  	age?:number
}
interface GetUserNameI{
	(u: User): string
}
  1. 매개변수 u(순서만 상관있음 이름중요 x)의 타입을 User로 지정
  2. 리턴하는 타입을 string으로 지정

이렇게 interface로 함수의 타입을 지정해줄 수 있다. 이를 호출 시그니처라고한다.

interface UserI{
	name: string
  	getName():string //메소드 타입 지정
}
class User implements UserI {
	public name
  	constructor(name:string){
    	this.name = name;
    }
  	getName(){
    	return this.name
    }
}
const user = new User('kim');
user.getName() // kim

function hello(userClass, msg:string){
	const user = new userClass('kim');
  	return `Hello ${user.getName()}, ${msg}`
}
hello(User, 'good morning!');

완전 자바잖아...? 😮
클래스는 타입을 요래요래 지정하면 된다

interface UserC {
	new (n: string): UserI // new로 생성한 생성자함수(클래스)는 UserI를 반환한다.
}

이를 생성(contructor) 시그니처라고 한다

인덱싱 가능 타입

튜플이나 배열을 뜻하는 거겠지?

interface ArrLike {
	[key: number]: string
}
const arrLike: ArrLike = {0:"A", 1:"B"};
arrLike[1] // B

//가변적인 인덱싱타입은 이렇게 사용한다
interface User{
  	//키에 문자, 값에 문자,숫자가 들어올수 있다
	[key: string]: number | string
  	//name, age가 문자와 숫자로 무조건 존재해야한다
  	name: string
  	age: number
}
const user:User = {
	name:"kim",
  	age:85,
  	email:...
  	city:"서울",
  	value: 2222,
  	isValid:true //존재할수 없는 타입이라 에러
}
function getValues(payload: //unknown이면 에러가 난다. Payload){
	if(payload && payload.constructor === Object){
    	return Object.keys(payload).map(key => payload[key]) // 이러면 오류가난다...
    }
}
//payload에 들어갈 타입을 지정해주어야한다...!
interface Payload {
	[key: string]: unknown;
}

인터페이스의 각 타입을 하나씩만 활용할수도 있다.

interface Test {
	name: string
  	age: number
}
const a: User['name'] = "kim" // kim

인터페이스 확장

클래스처럼 extends 키워드를 사용한다

interface UserA{
	name: string
  	age: number
}
interface UserB extends UserA{
	isValid: boolean
}
const user: UserB = {
	name: "kim",
  	age: 8,
  	isValid: true
}

함수

함수의 명시적 this타입

TS에서도 당연히 this를 쓸텐데, 명시적 this가 뭘까? 일반함수에서는 필요없을텐데 말이다.

interface User{
	name:string
}
function greet(msg:string){
	return `Hello${this.name}, ${msg}` //이렇게 사용하면 this가 전역객체기에 오류가난다
}
//수정후
function greet(this: User, msg:string){
	return `Hello${this.name}, ${msg}` 
}

음...JS와 용법이 달라서 헷갈린다. 많이 써보는 게 최고겠지?

함수 오버로딩

function addString(x: string, y:string): string{
	return x+y
}
function addString(x: number, y:number): number{
	return x+y
}
function addString(x: string, y:string): string
function addString(x: number, y:number): number
function any(x:any, y:any): any{
	return x+y
}

선언부를 위처럼 여러개 작성한다. 신기하구먼
interface에서도 함수 오버로딩이 가능하다

interface UserBase{
	name:string
  	age:number
}
interface User extends UserBase{
	updateInfo(newUser: UserBase): User
  	updateInfo(name: string, age: number): User
}
const user: User = {
	name: 'kim',
  	age: 85,
  	updateInfo: function(nameOrUser: UserBase | string, age?:number){
    	if(typeof nameOrUser === 'string' && age !+= undefined){
        	this.name = nameOrUser;
          	this.age = age;
        } else if (typeof nameOrUser === 'object'){
        	this.name = nameOrUser.name;
          	this.age = nameOrUser.age
        }
      	return this;
    }
}

클래스의 접근 제어자

자바처럼 public, private, protect, static, readonly가 있음.
이 접근제어자들은 모두 TS에서만 작동함.
=> 예를들어 js에서도 private하게 작동하고싶다면 #키워드 사용해서 private하게 만들어야함!

interface UserA {
	name: string,
  	age: number,
  	val: number
}

class User implement UserA {
  	private readonly name //setter로 재정의 금지.
    protected age // getter, setter를 따로 적어주어야 한다.
    val // public은 디폴트여서 작성하지 않아도 됨
	constructor(name: string, age: number, val: number){
    	this.name = name;
      	this.age = age;
      	this.val = val;
    }
}

//아래와 같이 중복되는 부분을 제거해줄수 있다.
class User implement UserA {
	constructor(
      private readonly name: string, 
      protected age: number, 
      val: number){}
}
privateprotectedpublic
클래스내부 호출ooo
자식클래스에서 호출xo
클래스 외부에서 호출xxo

이런차이가 있고 static클래스 자체의 멤버(인스턴스화 되면 없어짐)가 되는것이다. private와 헷갈릴 수 있지만, static
클래스 외부에서 호출 가능함

readonlycontructor내부에서 초기화 한 뒤 재할당 불가능이다.


추상 클래스

abstract class Animal{
	abstract name: string,
  	abstract age: number,
  	getName(){
    	console.log(this.name);
    }
  	abstract getAge(): number;
}

class Dog extends Animal{
	constructor(public color: string){
      super(name, age)
    }
  	//추상클래스 내부 abstract로 선언된 메서드는 무조건 구현해주어야함.
  	getAge(){
    	return this.age
    }
} 

const maltese = new Dog("rei", "11", "white")

언뜻보면 interface와 유사하다. 둘의 차이는 뭘까?

  • 추상클래스는 클래스다. 따라서 extends키워드로 상속받고 super키워드로 슈퍼클래스의 constructor를 호출해야함.
    interface는 클래스가 아니다. implement키워드로 구현해야할 멤버들을 받아 구현하면됨.
  • 추상클래스의 구현부는 없어도되고 있어도 된다. interface의 구현부는 없다.
  • 추상클래스는 클래스 접근 제어자(private, public...)을 가질 수 있음.
  • 추상클래스는 한번만 상속 가능하지만 interface는 여러개를 implement로 받아올수 있음.

요 차이다.


데코레이터

profile
모르는 것을 모른다고 하기

0개의 댓글