티스토리 뷰

공부

240630 ts 질문

민아! 2024. 6. 30. 16:27

1

type Operation2 = {
  (a: number, b: number): number;
  name: string;
};

const add2: Operation2 = (a, b) => a + b;
(...)

add2(1, 2);
add.name;
 

참고로 이때 다음과 같이 호출 시그니쳐 아래에 프로퍼티를 추가 정의하는 것도 가능합니다. 이렇게 할 경우 함수이자 일반 객체를 의미하는 타입으로 정의되며 이를 하이브리드 타입이라고 부릅니다.

 

 

 

2

type Dog = {
  name: string;
  isBark: boolean;
};

type Cat = {
  name: string;
  isScratch: boolean;
};

type Animal = Dog | Cat;

function warning(animal: Animal) {
  if ("isBark" in animal) {
    console.log(animal.isBark ? "짖습니다" : "안짖어요");
  } else if ("isScratch" in animal) {
    console.log(animal.isScratch ? "할큅니다" : "안할퀴어요");
  }
}​
2개의 타입 Dog와 Cat을 정의하고 두 타입의 유니온 타입인 Animal 타입을 정의했습니다.
다음으로 매개변수로 Animal 타입의 값을 받아 동물에 따라 각각 다른 경고를 콘솔에 출력하는 함수 warning을 만들어 주었습니다.
만약 이 함수를 호출하고 인수로 Dog 타입의 객체를 전달하면 “짖습니다” 또는 “안짖어요”를 출력할 것이고 Cat 타입의 객체를 전달하면 “할큅니다” 또는 “안 할퀴어요”를 출력할 것 입니다.
 
그런데 이렇게 in 연산자를 이용해 타입을 좁히는 방식은 좋지 않다고 이전에 살펴본 적 있습니다.
예를 들어 만약 Dog 타입의 프로퍼티가 다음과 같이 중간에 이름이 수정되거나 추가 또는 삭제될 경우에는 타입 가드가 제대로 동작하지 않을 수도 있습니다.
 
 

3

(...)

// Dog 타입인지 확인하는 타입 가드
function isDog(animal: Animal): animal is Dog {
  return (animal as Dog).isBark !== undefined;
}

// Cat 타입인지 확인하는 타입가드
function isCat(animal: Animal): animal is Cat {
  return (animal as Cat).isScratch !== undefined;
}

function warning(animal: Animal) {
  if (isDog(animal)) {
    console.log(animal.isBark ? "짖습니다" : "안짖어요");
  } else {
    console.log(animal.isScratch ? "할큅니다" : "안할퀴어요");
  }
}

 

isDog 함수는 매개변수로 받은 값이 Dog 타입이라면 true 아니라면 false를 반환합니다. 이때 반환값의 타입으로 animal is Dog 를 정의하면 이 함수가 true를 반환하면 조건문 내부에서는 이 값이 Dog 타입임을 보장한다는 의미가 됩니다. 따라서 warning 함수에서 isDog 함수를 호출해 매개변수의 값이 Dog 타입인지 확인하고 타입을 좁힐 수 있습니다.


4

interface Person {
  readonly name: string;
  age?: number;
  sayHi: () => void; 
  sayHi: (a: number, b: number) => void; // ❌
}



interface Person {
  readonly name: string;
  age?: number;
  sayHi(): void;
  sayHi(a: number): void;
  sayHi(a: number, b: number): void;
}
함수 타입 표현식으로 메서드의 타입을 정의하면 메서드의 오버로딩 구현이 불가능합니다.
그러나 호출 시그니처를 이용해 메서드의 타입을 정의하면 오버로딩 구현이 가능합니다.
 

 

 

5

class StudentDeveloper extends Student {
  // 필드
  favoriteSkill;

  // 생성자
  constructor(name, grade, age, favoriteSkill) {
    this.favoriteSkill = favoriteSkill;
  }

  // 메서드
  programming() {
    console.log(`${this.favoriteSkill}로 프로그래밍 함`);
  }
}
StudentDeveloper 클래스만의 새로운 필드나 메서드도 다음과 같이 정의할 수 있습니다.
그런데 이때 StudentDeveloper 클래스에서 Student 클래스의 생성자를 함께 호출해줘야 합니다. 그렇지 않으면 생성되는 객체의 name, grade, age 값이 제대로 설정되지 않습니다. 따라서 다음과 같이 super 라는 메서드를 호출합니다.
 
class StudentDeveloper extends Student {
  // 필드
  favoriteSkill;

  // 생성자
  constructor(name, grade, age, favoriteSkill) {
    super(name, grade, age);
    this.favoriteSkill = favoriteSkill;
  }

  // 메서드
  programming() {
    console.log(`${this.favoriteSkill}로 프로그래밍 함`);
  }
}
super를 호출하고 인수로 name, grade, age를 전달하면 슈퍼 클래스(확장 대상 클래스)의 생성자를 호출합니다. 따라서 this.name, this.grade, this.age의 값을 설정하게 됩니다.

 

 

 

 

6
타입스크립트에서 클래스의 상속을 이용할 때 파생 클래스(확장하는 클래스)에서 생성자를 정의 했다면 반드시 super 메서드를 호출해 슈퍼 클래스(확장되는 클래스)의 생성자를 호출해야 하며, 호출 위치는 생성자의 최상단 이어야만 합니다.
class ExecutiveOfficer extends Employee {
  officeNumber: number;

  constructor(
    name: string,
    age: number,
    position: string,
    officeNumber: number
  ) {
    super(name, age, position);
    this.officeNumber = officeNumber;
  }
}

 

 

7

/**
 * 인터페이스와 클래스
 */

interface CharacterInterface {
  name: string;
  moveSpeed: number;
  move(): void;
}

class Character implements CharacterInterface {
  constructor(
    public name: string,
    public moveSpeed: number,
    private extra: string
  ) {}

  move(): void {
    console.log(`${this.moveSpeed} 속도로 이동!`);
  }
}
인터페이스 CharacterInterface는 name, moveSpeed 프로퍼티와 move 메서드를 갖는 객체 타입을 정의합니다. 그런데 이 인터페이스를 클래스에서 implements 키워드와 함께 사용하면 이제부터 이 클래스가 생성하는 객체는 모두 이 인터페이스 타입을 만족하도록 클래스를 구현해야 합니다.

 

 

8.

function map(arr: unknown[], callback: (item: unknown) => unknown): unknown[] {}
메서드를 적용할 배열을 매개변수 arr로 받고, 콜백 함수를 매개변수 callback으로 받습니다.
map 메서드는 모든 타입의 배열에 적용할 수 있기 때문에 arr의 타입은 unknown[]으로 정의합니다. callback의 타입은 배열 요소 하나를 매개변수로 받아 특정 값을 반환하는 함수로 정의합니다. 함수 타입 표현식을 이용했습니다. 마지막으로 map 메서드의 반환값의 타입은 배열 타입으로 정의합니다.
다음으로는 이 함수에 타입 변수를 선언하여 제네릭 함수로 만듭니다.
// 모든 unknown 타입을 타입 변수 T로 대체합니다. 다음으로는 함수 내부를 구현합니다.
function map<T>(arr: T[], callback: (item: T) => T): T[] {}

// 1차적으로 map 함수를 만들고 타입 정의도 마쳤습니다. 이제 함수를 호출합니다.
function map<T>(arr: T[], callback: (item: T) => T): T[] {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(callback(arr[i]));
  }
  return result;
}

// 잘 동작하는 것 같습니다. 매개변수 arr에 number[] 타입의 배열을 제공하니 타입변수 T가 number로 추론되고 그 결과 map 함수의 반환값 타입도 number[]가 되었습니다.
const arr = [1, 2, 3];

function map<T>(arr: T[], callback: (item: T) => T): T[] {
  (...)
}

map(arr, (it) => it * 2);
// number[] 타입의 배열을 반환
// 결과 : [2, 4, 6]


// 콜백함수가 모든 배열 요소를 String 타입으로 변환하도록 수정했습니다. 이러면 오류가 발생합니다. 첫번째 인수로 arr을 전달했을 때 타입 변수 T에는 number 타입이 할당되었기 때문에 콜백 함수의 반환값 타입도 number 타입이 되어야 하기 때문입니다. 
// 그런데 map 메서드는 이렇게 원본 배열 타입과 다른 타입의 배열로도 변환할 수 있어야 합니다. 따라서 타입 변수를 하나 더 추가해 다음과 같이 수정합니다.
const arr = [1, 2, 3];

function map<T, U>(arr: T[], callback: (item: T) => U): U[] {
  (...)
}

map(arr, (it) => it.toString());
// string[] 타입의 배열을 반환
// 결과 : ["1", "2", "3"]


const arr = [1, 2, 3];

function map<T, U>(arr: T[], callback: (item: T) => U): U[] {
  (...)
}

map(arr, (it) => it.toString());
// string[] 타입의 배열을 반환
// 결과 : ["1", "2", "3"]

 

 

9

 

 

10

'공부' 카테고리의 다른 글

next.js란?  (0) 2024.06.02
React의 정의와 주요 특징  (0) 2024.05.08
Kakao Oauth Key 발급 방법  (0) 2024.05.05
Google Auth Key 발급 방법  (0) 2024.05.05
Node.js와 HTTP 서버 구축, Rest API  (0) 2024.05.02
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함