Skip to content

Latest commit

 

History

History
348 lines (274 loc) · 7.54 KB

4.md

File metadata and controls

348 lines (274 loc) · 7.54 KB

섹션 5 ~ 10 인터페이스/타입별칭/연산자를 이용한 타입/이넘/클래스/제네릭

1. 인터페이스(interface)

  • interface 이름 { }
  • 상호 간에 정의한 약속 혹은 규칙
interface User {
  name: string;
  age: number;
}

1.1 변수에 인터페이스 활용

const seho: User = { name: "hi", age: 100 };

1.2 함수에 인터페이스 활용

1.2.1 함수의 파라미터에 사용하는 경우

function getUser(user: User) {
  console.log(user);
}

const capt = {
  name: "캡틴",
  age: 100,
  id: 1, // User 인터페이스에 없던 속성 추가
};

getUser(capt);
  • 인터페이스에 정의된 속성, 타입의 조건만 만족한다면 객체의 속성 갯수가 더 많아도 상관 없다.

1.2.2 함수의 스펙(구조)에 사용하는 경우

  • 인자와 반환값의 타입을 정의
interface SumFunction {
  (a: number, b: number): number;
}
let sum: SumFunction;
sum = function (num1: number, num2: number): number {
  return num1 + num2;
};

1.3 인덱싱 방식을 정의하는 인터페이스

1.3.1 배열의 인덱싱에 사용하는 경우

interface StringArray {
  [index: number]: string;
}
let arr: StringArray;
arr[0] = "hi";
arr[1] = 10; // X

1.3.2 딕셔너리 패턴: 객체 접근 방식

interface StringRegexDictionary {
  [key: string]: RegExp;
}

const obj: StringRegexDictionary = {
  cssFile: /\.css$/,
  jsFile: /\.js$/,
};

1.4 인퍼테이스 확장(상속)

interface Person {
  name: string;
  age: number;
}

// interface Developer {
//   name: string;
//   age: number;
//   language: string;
// }

interface Developer extends Person {
  language: string;
}

const joo: Developer = {
  name: "joo",
  age: 20,
  language: "ts",
};

2. 타입 별칭

  • type 이름 = { }
  • 타입 별칭은 정의한 타입에 대해 나중에 쉽게 참고할 수 있게 이름을 부여하는 것과 같다.
  • 인터페이스와 비슷하지만 타입 별칭은 확장이 불가능하다.
type Person = {
  name: string,
  age: number,
};
let capt: Person = {
  name: "seho",
  age: 100,
};

3. 연산자를 이용한 타입

3.1 유니온 타입(Union Type) |

  • '~이거나'
  • 함수의 파라미터에 하나 이상의 타입을 적용하고 싶을 때 사용한다.
  • any보다는 명시적이다.
function logMessage(value: string | number) {
  console.log(value);
}
  • 단, 인터페이스와 같은 타입 구조체를 이용했을 때 유니온 타입으로 정의하면 타입의 공통된 속성만 제공한다.
interface Person {
  name: string;
  age: number;
}
interface Developer {
  name: string;
  skill: string;
}

function introduce(someone: Person | Developer) {
  someone.name; // O 정상 동작
  someone.age; // X 타입 오류
  someone.skill; // X 타입 오류
}
  • 왜냐하면 introduce() 함수를 호출하는 시점에 Person 타입이 올지 Developer 타입이 올지 알 수가 없기 때문에 어느 타입이 들어오든 간에 오류가 안 나는 방향으로 타입을 추론한다고 한다.
const capt: Person = { name: "capt", age: 100 };
introduce(capt);
// 만약 `introduce` 함수 안에서 `someone.skill` 속성을 접근하고 있으면 함수에서 오류 발생

3.2 인터섹션 타입

  • & 연산자를 이용해 여러 개의 타입 정의를 하나로 합치는 방식
interface Developer {
  name: string;
  skill: string;
}
interface Person {
  name: string;
  age: number;
}

function askSomeone(someone: Developer & Person) {
  someone.age;
  someone.name;
  someone.skill;
}

// Developer, Person 속성 모두를 가지는 새로운 타입 유형으로.
askSomeone({ name: "개발자", skill: "웹 개발", age: 27 });

4. 이넘

  • enum 이름 { }
  • 특정 값들의 집합을 의미하는 자료형
  • 드롭다운이나, 정해진 목록의 값들을 지정할 때 이넘을 쓰기 좋다.

4.1 숫자형

  • 별도의 값을 지정하지 않으면 숫자형 이넘으로 취급한다.
// 숫자형 이넘
enum Avengers {
  Capt,
  Ironman,
  Hulk,
}
const myHero = Avengers.Capt;
console.log(myHero) // 0
// 숫자는 자동으로 `1씩 증가

4.2 문자형

enum Shoes {
  Nike = '나이키',
  Adidas = '아디다스'
}
const myShoes = Shoes.Nike;
console.log(myShoes) // '나이키'

4.3 활용

  • 이넘에서 제공하는 데이터만 접근할 수 있게 된다.
enum Answer {
  Yes = "Y",
  No = "N",
}

function askQuestion(answer: Answer) {
  if (answer === Answer.Yes) {
    console.log("정답");
  }
  if (answer === Answer.No) {
    console.log("오답");
  }
}
askQuestion(Answer.Yes); // Y
askQuestion("Yes"); // X

5. 클래스

// ES5
function Person1(name, age) {
  this.name = name;
  this.age = age;
}
const hulk = new Person1("Banner", 33);

// ES6 + 타입스크립트
class Person {
  private name: string;
  public age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
const capt = new Person('Steve', 100);
  • 리액트에서도 훅 기반 함수형 문법을 주로 쓰고, 뷰에서도 그렇기 때문에 타입스크립트의 클래스 문법을 많이 쓰진 않을 것 같다고 함.

6. 제네릭

  • <type>
  • 타입을 마치 함수의 파라미터처럼 사용하는 것
  • 매번 타입을 정의하는 것이 아니라, 호출할 때 타입을 넘김으로써 어떤 타입이 와도 유연하게 타입을 사용할 수 있게 됨
function logText<T>(text: T): T {
  return text;
}

// 위에서 선언한 함수 호출 방법 2가지
// #1
const text = logText<string>("Hello Generic");
// #2
const text = logText("Hello Generic");

logText<string>('hi');
// 위 코드는 아래 코드와 동일한 의미
function logText<string>(text: string): string {
  return text;
}

6.2 제네릭의 타입 제한

  • 아래 같은 예시는 제네릭으로 받은 타입을 파라미터와 반환값에서 배열로 활용하겠다고 명시적으로 나타냄
  • 따라서 배열의 특정 속성에 접근할 수 있게 됨
function getNumberAndArray<T>(value: T): T {
  value.length; // X
  return value;
}

function getNumberAndArray2<T>(value: T[]): T[] {
  value.length; // O
  return value;
}
getNumberAndArray2 < string > ["hi", "abc"];

6.3 정의된 타입으로 타입 제한하기

  • 정의된 인터페이스를 상속 받으면, 해당 인터페이스의 속성은 기본으로 가지면서 제네릭으로 추가로 속성을 정의할 수 있는 상태가 됨
interface LengthType {
  length: number;
}
function getNumberAndArray3<T extends LengthType>(text: T): T {
  text.length;
  return text;
}

6.4 keyof로 제네릭의 타입 제한하기

  • keyof를 사용하면 인터페이스 ShoppingItems에 포함된 key만 인자로 전달할 수 있게 됨
interface ShoppingItems {
  name: string;
  price: number;
  address: string;
  stock: number;
}
function getAllowedOptions<T extends keyof ShoppingItems>(option: T): T {
  if (option === 'name' || option === 'address') {
    console.log('option type is string');
    return option;
  }
  if (option === 'price' || option === 'stock') {
    console.log('option type is number');
    return option;
  }
}
getAllowedOptions('nothing'); // X