타입스크립트 공부 (2)

최자은·2023년 6월 28일
0

타입스크립트

목록 보기
3/5
  • Object와 {}
// Object와 {}는 모든 타입 (null, undefined 제외)
const a: {} = 'sdt' // 통과
const b: Object = 4 // 통과

// 실제 객체는 object 소문자
const c: object = '4' // 에러
const d: object = {hello: 'world'} // 통과
// @@ 하지만 실제 상황에서 object로 사용하는 것은 지양하고, interface / type / class 쓰는 것이 좋음.
  • 인덱스드 시그니처, 맵드 타입스
// 인덱스드 시그니처
type A = { a: string; b: string; c: string };
type B = { [key: string]: string }; // 키가 다 string이고 그 값고 string

type C = "Human" | "Animal" | "Mammal";
type D = { [key in C]: number };

const test: D = { Animal: 4, Human: 1, Mammal: 10 };
  • 클래스 기능
// 1.
class A {
  a: string;
  b: number;

  constructor(a: string, b: number = 123) {
    this.a = a;
    this.b = b;
  }

  method() {}
}

// 클래스 자체의 타입
typeof A;
// 클래스의 이름은 인스턴스를 가리킴
const test: A = new A("asd");


// 2.
interface A {
  readonly a: string;
  b: string;
}

// implements : 구현하다
// 인터페이스를 구현하다. 인터페이스의 구성을 갖춰야함
// 클래스의 모양을 인터페이스로 통제할 수 있다.
class B implements A {
  private a: string = "123";
  protected b: string = "asdf";
  c: string = "wow";

  method() {
    console.log(this.a);
  }
}

class C extends B {
  method() {
    console.log(this.a); // private. 상속한 애들도 접근 불가
    console.log(this.b); //protected는 상속한 애들은 접근 가능
    console.log(this.c);
  }
}

new C().c; // public. 밖에서도 안에서도 접근 가능
new C().a; // private. 클래스 안에서만 쓸 수 있음.
new C().b; // protected. 클래스 안에서만 쓸 수 있음.
//private과 protected 차이 ? => 상속 받았을 때 쓸 수 있냐 없냐로 구분

// 3. 추상 클래스
// abstract라고 되어 있는 부분은 반드시 상속 받았을 때 선언해줘야 한다.
abstract class B {
  private readonly a: string = "123";
  b: string = "asdf";
  c: string = "wow";

  abstract method(): void;
  method2() {
    return "3";
  }
}

class C extends B {
  method() {
    console.log(this.b);
    console.log(this.c);
  }
}
  • private vs protected
  • 옵셔널, 제네릭
function abc(a: number, b?: number, c?: number) {}

abc(1);
abc(1, 2);
abc(1, 2, 3);
abc(1, 2, 3, 4); // 에러

// 4개도 가능하게 하는 방법
function abcd(...arg: number[]) {}
  • 제네릭 활용 예시
    코드 작성 시에는 타입이 뭐가 될지 모르지만, 코드 실행하면 확실히 알게된다.
// 1. forEach
// 인터페이스도 제네릭 쓸 수 있음
interface Array<T> {
  forEach(
    callbackfn: (value: T, index: number, array: T[]) => void,
    thisArg?: any
  ): void;
}

const a: Array<number> = [1, 2, 3];

a.forEach((value) => {
  console.log(value); // 1, 2, 3
});

["1", "2", "3"].forEach((value) => {
  console.log(value); // '1', '2', '3'
});

["1", 2, false].forEach((value) => {
  console.log(value); // "1", 2, false
});

// 2.
function add<T>(x: T, y: T): T {
  return x + y;
}

add<number>(1, 2); // 타입을 직접 알리는 경우 - 타입스트립트가 추론을 잘 못해줄 때.
add("1", "2");

// 3. map
interface Array<T> {
  map<U>(
    callbackfn: (value: T, index: number, array: T[]) => U,
    thisArg?: any
  ): U[];
}

const strs = [1, 2, 3].map((value) => value.toString());

// 4. filter
interface Array<T> {
  filter<S extends T>(
    predicate: (value: T, index: number, array: T[]) => value is S,
    thisArg?: any
  ): S[];
  filter(
    predicate: (value: T, index: number, array: T[]) => unknown,
    thisArg?: any
  ): T[];
}

// const strs = [1, 2, 3].filter((value) => value % 2 !== 0);
// const strs = [1, "3", 2, "5", 3].filter((value) => typeof value === "string");
const predicate = (value: string | number): value is string =>
  typeof value === "string";
const strs = [1, "3", 2, "5", 3].filter(predicate); // ['3', '5'] string[]
  • forEach 타입 직접 만들어 보기
interface Arr<T> {
  forEach(callbackfn: (item: T) => void): void;
  forEach(
    callbackfn: (value: T, index: number, array: T[]) => void,
    thisArg?: any
  ): void;
}

const a: Arr<number> = [1, 2, 3];

a.forEach((item) => {
  console.log(item);
});

a.forEach((item) => {
  console.log(item);
  return 3;
});

const b: Arr<string> = ["1", "2", "3"];

b.forEach((item) => {
  console.log(item);
});

b.forEach((item) => {
  console.log(item);
  return "3";
});
  • map 타입 직접 만들어 보기
interface Arr<T> {
  map<S>(callbackfn: (item: T, index: number) => S): S[];
}

const a: Arr<number> = [1, 2, 3];

const b = a.map((item, index) => item + 2); // [1, 2, 3]
const c = a.map((item, index) => a.toString()); // ['1', '2', '3']
const d = a.map((item, index) => item % 2 === 0); // [false, true, fasle]

const e: Arr<string> = ["1", "2", "3"];
const f = e.map((item) => item + 2);
  • filter 타입 직접 만들어 보기
interface Arr<T> {
  map<S>(callbackfn: (item: T, index: number) => S): S[];
  filter<S extends T>(callbackfn: (item: T) => item is S): S[];
}

const a: Arr<number> = [1, 2, 3];

const b = a.filter((item): item is number => item % 2 === 0); // [2]

const c: Arr<number | string> = [1, "2", 3];

// 커스텀 타입가드의 형식 조건자로 만들어 줘야함
// 형식 조건자가 커스텀 타입가드
// 1) item is number, item is string
const d = c.filter((item): item is string => typeof item === "string"); // ["2"];
// 2) 밖에 빼서 사용할 수도 있음
const predicate = (item: string | number): item is number =>
  typeof item === "number";
const e = c.filter(predicate); // [1, 3]
  • 공변성, 반공변성
    함수 간에 서로 대입할 수 있냐, 없냐를 따지는 것
// 1.
function a(x: string): number {
  return +x;
}

a("2"); // 2

type B = (x: string) => number | string;
const b: B = a; // 대입이 됨
// 즉, 리턴값은 더 넓은 타입으로 대입할 수 있다.

// 2.
function a(x: string): number | string {
  return +x;
}

a("2"); // 2

type B = (x: string) => number;
const b: B = a; // 대입안됨
// 즉, 리턴값이 더 넓은 타입에서 좁은 타입으로 대입할 수 없다.

// 3. 
function a(x: number | string): number {
  return +x;
}

type B = (x: string) => number;
const b: B = a; // 대입 가능
// 매개변수는 좁은 타입에서 넓은 타입으로 대입 가능하다
// 매개변수는 매개변수의 타입을 하나로 보고 좁은 타입으로 대입된다.
// 리턴값은 좁은타입 -> 넓은타입으로,
// 매개변수는 좁은 타입으로

// 최종
function a(x: number | string): number {
  return +x;
}

type B = (x: string) => number | string;
const b: B = a; // 대입 가능
  • 오버로딩
    같은 타입을 여러 번 선언하는 것
interface Add {
  (x: number, y: number): number;
  (x: string, y: string): string;
}

const add: Add = (x, y) => x + y;

class A {
  // 오버로딩
  add(x: number, y: number): number;
  add(x: string, y: string): string;
  // 실제 구현부
  add(x: any, y: any) {
    return x + y;
  }
}

const a = new A().add("1", "2");
profile
모든 과정을 기록하며 꾸준히 성장하고 실수를 반복하지 말자 !

0개의 댓글