[항해 플러스 프론트엔드 7기] 사전 스터디 3주차 - TS

Jaehyun Ahn·2025년 10월 1일
0

항해 플러스

목록 보기
5/7

제네릭

타입 파라미터를 사용하여 코드 작성 시점에서는 구체적인 자료형을 정하지 않고, 사용 시점에 타입을 지정

  • <T> 형태로 많이 작성
  • 타입을 매개변수처럼 다루는 문법
  • 타입을 유동적으로 받기 때문에 코드 재사용성이 높아진다.
  • 제네릭으로 허용되는 타입은 extends를 사용하여 제한할 수도 있다. (인터페이스 확장의 extends와 반대됨)

유틸리티 타입

Partial

: 타입의 모튼 프로퍼티를 옵셔널하게 만든다. (부분 집합 느낌)

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

type TestPartial = Partial<Test>;

Required

: 타입의 모든 프로퍼티를 필수로 만든다

interface Test {
  name?: string;
  age?: number;
}

type TestRequired = Required<Test>;

Readonly

: 타입의 모든 프로퍼티를 읽기 전용으로 만든다.

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

type TestReadonly = Readonly<Test>; // { readonly name: string; readonly age: string }

Record

: Record<K, T> => K 키 집합으로부터 T 값의 객체 생성

Pick

: 지정된 프로퍼티만 가져온다

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

type TestPick = Pick<Test, 'name'>; // {name: string}

Omit

: 지정된 타입을 무시한다

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

type TestOmit = Omit<Test, 'name'>; // {age: number}

Parameters

: 함수 타입의 매개변수를 가져온다.

function doSmth(value: string, anotherValue: number): string {
  return 'test';
}
type Params = Parameters<typeof doSmth>; // {value: string, anotherValue: number}

ReturnType

: 함수의 반환 타입을 가져온다.

type Return = ReturnType<typeof doSmth>; // string

조건부 타입

: 어떤 타입이 다른 타입과 일치하거나 확장하는지에 따라 타입을 조건부로 설정하는 것


type A = number extends number ? 'Yes' : 'No'; 
// 'Yes'
type B = string extends number ? 'Yes' : 'No'; 
// 'No'

조건부 + infer (타입 추론)

: 조건부 타입 안에서 infer 키워드를 써서 새로운 타입 변수 선언이 가능하다


type Return<T> = T extends (...args: any[]) => infer R ? R : never;

type R1 = Return<() => number>; // number
type R2 = Return<(x:string) => Promise<boolean>>; // Promise<boolean>

튜플 옵셔널 요소와 나머지

: ?를 사용하여 옵셔널한 요소를 튜플로 선언하고 나머지는 ...을 사용하여 다른 타입에 따라 선언할 수 있다.


// 만약 튜플의 길이를 정확히는 모르지만 적어도 1 이상인 경우, `?`를 사용하여 선택적인 타입을 지정할 수 있습니다.
const list: [number, number?, boolean?] = [];
list[0] // number
list[1] // number | undefined
list[2] // boolean | undefined
list[3] // Type error: Tuple type '[number, (number | undefined)?, (boolean | undefined)?]' of length '3' has no element at index '3'.

// 기존 타입을 기반으로 튜플을 만들 수도 있습니다.
// 만약 배열의 시작 부분을 패딩하고 싶다면, rest 연산자 `...`를 사용하여 패딩할 수 있습니다.
function padStart<T extends any[]>(arr: T, pad: string): [string, ...T] {
  return [pad, ...arr];
}

const padded = padStart([1, 2], 'test'); // [string, number, number]

튜플 : TS에서 길이와 각 원소의 타입이 고정된 배열

추상 클래스와 메소드

: 클래스와 그 안의 메서드는 추상적으로 선언하여 인스턴스화되지 않도록 할 수 있다.

abstract class Animal {
  abstract makeSound(): void;

  move(): void {
    console.log('roaming the earth...');
  }
}

// 확장 시 추상 메서드를 구현해야 합니다.
class Cat extends Animal {} // Compile error: Non-abstract class 'Cat' does not implement inherited abstract member 'makeSound' from class 'Animal'.

class Dog extends Animal {
  makeSound() {
    console.log('woof');
  }
}

// 추상 클래스는 인터페이스처럼 인스턴스화할 수 없으며 추상 메서드도 호출할 수 없습니다.
new Animal(); // Compile error: Cannot create an instance of an abstract class.

const dog = new Dog().makeSound(); // "woof"

생성자 시그니처

: 클래스 선언 외부에서 생성자 타입을 정의. 대부분의 경우 사용해서는 안된다.

ConstructorParameters 유틸리티 타입

: 클래스가 아닌 생성자 타입에서 생성자 매개변수를 가져오는 타입스크립트 헬퍼 함수이다.


TypeScript 4.0

가변 튜플 타입

: 튜플의 나머지 요소가 제네릭 일 수 있는 것. 여러 개의 rest 요소 사용이 허용된다.

declare function concatNew<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U];

레이블링된 튜플 요소

: 튜플 요소의 이름을 [start: number, end: number]와 같이 지정할 수 있다. 요소 중 하나에 이름이 지정된 경우, 모든 요소에 이름을 지정해야 한다.

type Foo = [first: number, second?: string, ...rest: any[]];

// 이렇게 하면 여기에서 인자의 이름을 올바르게 지정할 수 있으며 에디터에도 표시됩니다.
declare function someFunc(...args: Foo);

생성자로부터 클래스 프로퍼티 추론

: 생성자에서 프로퍼티를 설정하면 타입을 유추할 수 있으므로 수동으로 설정할 필요가 없다.

class Animal {
  // 생성자에서 타입을 할당할 때 타입을 설정할 필요가 없습니다.
  name;

  constructor(name: string) {
    this.name = name;
    console.log(this.name); // string
  }
}

JSDoc @deprecated 지원

: JSDoc, TSDoc @deprecated 태그는 타입스크립트에서 인식된다

/** @deprecated Use `NewType` instead */
type OldType = string;

type NewType = string;

const a: OldType = "hello"; // IDE에서 경고: OldType은 deprecated됨

TypeScript 4.1

템플릿 리터럴 타입

: 템플릿 리터럴을 사용하여 문자열 리터럴 타입을 조합/변형할 수 있는 기능

type ex = `Hello ${string}`;

// ex는 Hello로 시작하는 모든 문자열 타입

유니온 타입과 결합하여 조합이 가능하다.

type Lang = "ko" | "en";
type Path = `/api/${Lang}`;

// 결과: "/api/ko" | "/api/en"

제네릭, 다른 유틸리티와 결합이 가능하다.

type EventName<T extends string> = `on${Capitalize<T>}`;
type E = EventName<"click" | "hover">;
// "onClick" | "onHover"

// Capitalize : 문자열 리터럴 타입의 첫 글자를 대문자로 바꿔주는 유틸리티

매핑된 타입에서의 키 리매핑

: as 키워드를 사용해서 매핑된 타입의 키 이름을 다시 지정할 수 있다.

const obj = { value1: 0, value2: 1, value3: 3 };
const newObj: { [Property in keyof typeof obj as `_${Property}`]: number }; // { _value1: number; _value2: number; _value3: number; }

재귀적인 조건부 타입

: 자기 자신을 참조하여 타입을 점진적으로 변환하거나 분해하는 방식, 자기 자신을 다시 호출하여 복잡한 타입 연산을 수행하는 기법

type Last<T extends any[]> = 
  T extends [...infer Rest, infer L] 
    ? Last<Rest> extends never 
      ? L 
      : Last<Rest>
    : never;

// 결과
type A = Last<[1, 2, 3]>; // 3
type B = Last<['a', 'b', 'c', 'd']>; // 'd'

/// Last는 배열을 Rest와 마지막 L로 쪼갠 뒤, 재귀적으로 들어가면서 최종 L을 반환

jsDoc @see 태그를 지웒는 에디터

: 에디터에서 @see variable/type/link 태그가 지원된다.

@see : 참고 장보를 제공할 때 사용됨


/**
 * @see User
 */
type UserId = string;

interface User {
  id: UserId;
  name: string;
}

// UserId 볼 때 User 타입도 봐라

tsc -explainFiles

: ts 컴파일러에서 tsc -explainFiles 을 사용할 수 있다.

tsc -explainFiles : 컴파일에 포함된 파일과 그 이유를 설명

분해된 변수는 명시적으로 사용하지 않는 것으로 표시 가능

: 구조 분해할 때 밑줄(-)을 사용하여 변수를 사용하지 않을 것이라고 표시할 수 있음.

const [_first, second] = [3, 5];
console.log(second);

const [_, value] = [3, 5];
console.log(value);

TypeScript 4.3

프로퍼티에서 쓰기 타입 분리

: gettersetter의 타입을 다르게 설정 가능

  • getter 반환 타입은 setter 파라미터 타입의 supertype이어야 함 (읽을 때는 넓은 타입, 쓸 때는 좁은 타입 가능)
class Person {
  private _name: string = "";

  get name(): string {
    return this._name;
  }

  set name(value: string | undefined) {
    this._name = value ?? "";
  }
}

const p = new Person();
p.name = undefined; // OK (setter 허용)
console.log(p.name.toUpperCase()); // OK (getter는 string 반환)


// 읽을때는 무조건 string로 다루고, 쓸 때는 undefined도 받을 수 있도록

override

: 클래스 상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의할 때 사용

  • 상속 시 부모 클래스의 메서드를 그대로 가져오되, 동작을 바꾸고 싶을 때 override 사용
  • override 사용 시, 부모 클래스에 동일한 메서드가 없으면 에러 발생

class Animal {
  makeSound() {
    console.log("Some sound");
  }
}

class Dog extends Animal {
  override makeSound() {
    console.log("Bark!");
  }
}

const dog = new Dog();
dog.makeSound(); // Bark!

정적 인덱스 시그니처

: static [propsName: string] : string 를 사용하여 정적 인덱스 시그니처를 설정할 수 있다.

  • 일반 인덱스 시그니처와 달리, 타입 수준에서 특정 집합의 키만 허용하고, 그 값의 타입을 지정하는 형태
  • 정해진 키 집합에 대한 값 타입을 제한
type Roles = "admin" | "editor" | "viewer";

type RolePermissions = {
  [R in Roles]: boolean; // 각 역할은 boolean 값
};

const perms: RolePermissions = {
  admin: true,
  editor: false,
  viewer: true,
  // superAdmin: false, // ❌ Roles에 없는 키는 에러
};

// [R in Roles] => 정적 인덱스 시그니처
// 키 집합이 미리 정의

인덱스 시그니처 : 객체가 어떤 키를 가질 수 있는지, 값의 타입이 무엇인지 동적으로 정의하는 방법
[key : string] : number => 모든 문자열 키에 대해 number 타입 값 허용

: jsDoc, tsDoc에서 @link 태그 지원

@link : 코드나 문서, URL 등 외부 참조를 연결할 때 사용 (하이퍼링크 연결)

profile
미래 프론트 어쩌고

0개의 댓글