[Unit 5] TypeScript(2)

JeongYeon·2023년 5월 31일
0

[SEB FE]section4

목록 보기
13/16
post-thumbnail

TypeScript 열거형(Enum)

TypeScript 열거형(Enum)
Enum은 특정 값의 집합을 정의할 때 사용한다.

// Enum 형태
enum Color {
  Red,
  Green,
  Blue,
}

숫자형 열거형(Enum)
열거형은 숫자형과 문자열형이나 둘의 조합으로 정의될 수 있다.
디폴트값으로는 숫자형을 사용하고 값은 자동으로 0부터 1씩 증가한다.
수동으로도 값을 지정할 수 있다.

enum Color {
  Red = 1,
  Green = 2,
  Blue = 4,
}
let c: Color = Color.Green;
let greenValue: number = Color.Green;
let blueValue: number = Color.Blue;
console.log(c);          // 출력: 2
console.log(greenValue);  // 출력: 2
console.log(blueValue);   // 출력: 4

문자형 열거형(Enum)
문자형 열거형은 갑을 전부 다 특정 문자 또는 다른 열거형 값으로 초기화해야 한다.

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
let myDirection: Direction = Direction.Up;
console.log(myDirection); // 출력: "UP"

문자형 열거형에는 숫자형과 달리 auto-incrementing이 없지만 항상 명확한 값이 나와 읽기 편하다.
역 매핑(Reverse mappings)
숫자형 열거형에만 존재하는 특징으로 키(key)로 값(value)을 얻을 수 있고 값으로도 키를 얻을 수 있다.

enum Enum {
    A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"

TypeScript 인터페이스(Interface)

Interface
TypeScript에서 인터페이스는 타입체크를 위해 주로 사용된다.
인터페이스는 변수, 함수, 클래스에 사용할 수 있다.

💡예약어: 컴퓨터 프로그래밍 언어에서 이미 문법적인 용도로 사용되고있어 식별자로 사용할 수 없는 단어를 의미한다.

변수와 인터페이스
변수를 선언할 때 인터페이스를 사용할 수 있다.
TypeScript에서 인터페이스는 객체의 구조를 정의하기 위해 주로 사용되는 예약어이다.

interface User {
	name: string;
	age: number;
}
// 정상적으로 선언됩니다.
const user: User = {
	name: "anna",
	age: 20
}
// 프로퍼티의 순서를 지키지 않아도 정상적으로 선언됩니다.
const user: User = {
	age: 20,
	name: "anna"
}
// 정의된 프로퍼티보다 적게 작성했기 때문에 에러가 납니다.
const user: User = {
	name: "anna"
}
// 정의된 프로퍼티보다 많이 작성했기 때문에 에러가 납니다.
const user: User = {
	name: "anna",
	age: 20,
	job: "developer"
}

인터페이스를 만들때 예약어를 작성하고 인터페이스의 이름은 대문자로 작성한다.
이름을 대문자로 시작하는 것은 네이밍 컨벤션이다.

💡네이밍 컨벤션 : 이름을 짓는 일종의 관례로 대부분의 개발자는 인터페이스의 이름을 대문자로 시작하도록 작성한다. 인터페이스는 객체의 타입을 정의하고, 객체가 대문자로 시작하는 것과 유사하기 때문에 일관성 있는 코드 작성을 위해 이러한 관례를 따른다.

또한 인터페이스로 정의된 속성만 지정할 수 있다.
만약 프로퍼티를 선택적으로 사용하고 싶으면 ?연산자를 사용하면 된다.

interface User {
	name: string;
	age?: number;
}
// 정상적으로 선언됩니다.
const user: User = {
	name: "anna"
}

함수와 인터페이스
인터페이스를 사용해 객체의 프로퍼티 이름과 타입을 정의하고, 함수의 매개변수 타입과 반환 타입도 정의할 수 있다.

interface User {
	name: string;
	age: number;
	job: string;
}
interface Greeting {
	(user: User, greeting: string): string;
}
const greet: Greeting = (user, greeting) => {
	return `${greeting}, ${user.name}! Your job : ${user.job}.`;
}
const user: User = {
	name: "anna",
	age: 30,
	job: "developer"
};
const message = greet(user, "Hi");
console.log(message);

클래스와 인터페이스
클래스에서도 인터페이스를 사용할 수 있다.

interface Calculator {
	add(x: number, y: number): number;
	substract(x: number, y: number): number;
}
class SimpleCalculator implements Calculator {
	add(x: number, y:number) {
		return x + y;
	}
	substract(x: number, y: number) {
		return x - y;
	}
}
const caculator = new SimpleCalculator();
console.log(caculator.add(4, 9)); //13
console.log(caculator.substract(10, 5)); //5

Caculator 인터페이스를 사용하고 있기 때문에 SimpleCaculator 클래스 내에는 Calculator 인터페이스 내에 정의된 두 메서드를 반드시 작성해야 한다.
클래스를 구현할때 인터페이스에 정의된 함수나 메서드의 매개변수 타입과 반환 값이 일치하도록 구현해야한다.
클래스 내부에서 해당 메서드의 매개변수 타입을 한번 더 명시해주지 않으면 컴파일 에러가 발생한다.


인터페이스와 상속
인터페이스도 extends키워드를 사용해 기존에 존재하는 인터페이스를 상속해 확장 가능하다.

interface Person {
    name: string;
    age: number;
}
interface Developer extends Person {
    language: string;
}
const person: Developer = {
    language: "TypeScript",
    age: 20,
    name: "Anna",
}

여러 인터페이스를 상속받아 확장하는 것도 가능하다.

interface FoodStuff {
    name: string;
}
interface FoodAmount {
    amount: number;
}
interface FoodFreshness extends FoodStuff, FoodAmount {
	   isFreshed: boolean;
}
const food = {} as FoodFreshness;
food.name = "egg";
food.amount = 2;
food.isFreshed = true;

TypeScript 타입별칭(Type Aliases)

타입별칭
타입의 새로운 이름을 만드는 것으로 , 기존의 타입을 참조한다는 것을 의미한다.
타입별칭을 이용해 타입의 새로운 이름을 만들 때 type을 사용한다.

type MyString = string;
let str1: string = 'hello!';
// string 타입처럼 사용할 수 있습니다.
let str2: MyString = 'hello world!';

타입을 정의할 수 있는 모든 곳에는 타입 별칭을 쓸 수 있다.

type Person = {
  id: number;
  name: string;
  email: string;
}
//Commentary 인터페이스에서 Person 타입을 참조하고 있습니다.
interface Commentary {
  id: number;
  content: string;
  user: Person;
}
//객체에서 Commentary 인터페이스를 참조하고 있습니다.
let comment1: Commentary = {
    id: 1,
    content: "뭐예요?",
    user: {
        id: 1,
        name: "김코딩",
        email: "kimcoding@codestates.com",
    },
}
//Commentary 인터페이스 내부에 content 프로퍼티가 존재하기 때문에 
//content 프로퍼티를 작성하지 않으면 컴파일 에러가 납니다.
let kimcoding: Commentary = {
    id: 1,
    user: {
        id: 1,
        name: "김코딩",
        email: "kimcoding@codestates.com",
    },
};
//Person 타입 내부에 isDeveloper 프로퍼티가 존재하지 않기 때문에 
//isDeveloper 프로퍼티를 작성할 시 컴파일 에러가 납니다.
let kimcoding: Commentary = {
    id: 1,
    content: "뭐예요?",
    user: {
        id: 1,
        name: "김코딩",
        email: "kimcoding@codestates.com",
        isDeveloper: true,
    },
};

타입별칭으로 만들어진 타입을 참조할 때 인터페이스와 마찬가지로 내부에 정의된 프로퍼티를 전부 참조해야 한다.


인터페이스 vs. 타입 별칭

type Person = {
    name: string;
    age: number;
}
interface User {
    name: string;
    age: number;
}
//에러가 발생합니다.
type Students extends Person {
    className: string;  
}
//정상적으로 동작합니다.
interface Students extends User {
	 className: string;   
}
//정상적으로 동작합니다.
interface Students extends Person {
    className: string;  
}

타입 별칭은 타입에 새로운 이름을 부여하는 것까지만 하기 때문에 확장이 되지 않지만
인터페이스는 확장이 가능하다.


TypeScript 타입추론(Type Inference)

타입추론(Type Inference)
타입 추론은 변수나 함수의 타입을 선언하지 않아도 자동으로 유추하는 기능이다.

let isNumber = 123;

이 경우 타입스크립트는 isNumber의 타입을 자동으로 숫자로 추론한다.

최적 공통 타입(Best common type)
여러 표현식에서 타입추론이 발생할때 해당 표현식의 타입을 사용해 '최적 공통 타입'을 계산한다.

let x = [0, 1, null];

최적 공통 타입 알고리즘은 각 후보의 타입을 고려해 모든 후보의 타입을 포함할 수 있는 타입을 선택한다.

문맥상의 타이핑(Contextual Typing)
문맥상으로 타입을 결정하는 것으로 문맥상의 타이핑은 코드의 위치를 기준으로 일어난다.

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

add함수는 두개의 매개변수를 받아 더한 값을 반환한다. 매개변수의 타입이 명시되어 있지 않지만 타입스크립트는 타입을 자동으로 추론할 수 있다.

타입 추론 장점
1) 코드의 가독성 향상
2) 개발 생산성 향상
3) 오류 발견 용이성

타입 추론의 단점
1) 타입 추론이 잘못될 경우 코드 오류 발생
2) 명시적인 타입 지정이 필요한 경우가 있음


TypeScript 클래(Class)

TypeScirpt에서의 클래스(Class)
타입스크립트에서는 클래스의 속성과 메서드에 대한 타입을 명시할 수 있다.

class Person {
  name: string;
  age: number;
constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  greet(): void {
    console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
  }
}

클래스와 상속(Inheritance)
타입스크립트의 클래스는 인터페이스와 마찬가지로 기존에 존재하는 클래스를 상속받아 확장해 새로운 클래스르 만들 수 있다.
extends키워드를 사용해 상속한다.

class Animal {
    move(distanceInMeters: number): void {
        console.log(`${distanceInMeters}m 이동했습니다.`);
    }
}
class Dog extends Animal {
    speak(): void {
        console.log("멍멍!");
    }
}
const dog = new Dog();
dog.move(10);
dog.speak();

public, private 키워드
기본적으로 클래스 내에 선언된 멤버는 외부로 공개되는 것이 디폴트 값이다.
public으로 명시적으로 표시해 줄 수도 있다.

class Person {
  public name: string;
  public age: number;
  costructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  greet(): void {
    console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
  }
}

외부에 드러내고 싶지 않다면 private키워드로 명시해 주면 된다.

class Person {
  public name: string;
  private age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  greet(): void {
    console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
  }
}

readonly 키워드
읽기전용 키워드로 읽기 전용 프로퍼티들은 선언 또는 생성자에서 초기화해야 한다.
값을 변경할 수 없고, 변경되면 안 될 값을 readonly로 명시해 보호할 수 있다.

class Mydog {
    readonly name: string;
    constructor(theName: string) {
        this.name = theName;
    }
}
let spooky = new Mydog("스푸키");
spooky.name = "멋진 스푸키"; // 에러

TypeScript 제네릭(Generic)

TypeScript의 제네릭(Generic)
코드 재사용성을 높이고 타입 안정성을 보장하는 기능

함수나 클래스 작성 시 사용될 데이터의 타입을 미리 지정하지 않고 호출할때 인자로 전달된 데이터의 타입에 따라 자동으로 타입을 추론한다.

//제네릭 사용
function printLog<T>(text: T): T {
	return text;
}
const str = printLog<string>('hello');
//타입추론 기능 활용
const str = printLog('hello');

타입 추론 기능은 타입이 복자해져 컴파일러가 타입을 유추 할 수 없게 되는 경우에는 사용할 수 없다.

인터페이스와 제네릭

interface Item<T> {
	name: T;
	stock: number;
	selected: boolean;
}
const obj: Item<string> = { 
	name: "T-shirts",
	stock: 2, 
	selected: false
};
const obj: Item<number> = { 
	name: 2044512,
	stock: 2, 
	selected: false
};

클래스와 제네릭

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

제네릭 타입 변수

function printLog<T>(text: T[]): T[] {
	console.log(text.length);
	return text;
}
//명시적 작성
function printLog<T>(text: Array<T>): Array<T> {
	console.log(text.length);
	return text;
}

제네릭 제약 조건
extends를 이용해 작성하면 타입에 대한 강제는 아니지만 length에 대해 동작하는 인자만 넘겨받을 수 있다.

interface TextLength {
	length: number;
}
function printLog<T extends TextLength>(text: T): T {
	console.log(text.length);
	return text;
}

keyof를 이용한 제약

interface Item<T> {
	name: T;
	stock: number;
	selected: boolean;
}
function printLog<T extends keyof Item>(text: T): T {
	return text;
}
printLog('name'); //정상
pirntLog('key'); //에러

첫번째 인자로 받는 객체에 없는 속성들은 접근할 수 없게 제한할 수 있다.


내용 출처, 참조 : 코드스테이츠

profile
프론트엔드 개발자 될거야( ⸝⸝•ᴗ•⸝⸝ )੭⁾⁾

0개의 댓글

관련 채용 정보