# typescript

# hello TS world

npm init # node 초기화
npm i @types/node # 타입스크립트 설치
npm install typescript -g # 컴파일러 설치
# -g (글로벌 옵션) : 컴퓨터 모든곳에서 사용 가능
tsc -v # 버전 확인
1
2
3
4
5

src.index.ts

console.log("hello typescript");
const a: number = 5;
1
2
tsc src/index.ts # 컴파일
node src/index.js # 실행
1
2

# 컴파일과 실행을 한번에 tsx

npm i -g tsx
tsx -v
tsx src/index.ts
1
2
3

# tsconfig.json 파일 만들기

tsc --init
1
`tsconfig.json`
{
  "compilerOptions": {
    "target": "ESNext", // 변환되는 자바스크립트 버전
    "module": "ESNext", // 변환되는 자바스크립트 코드가 사용할 모듈 시스템버전
    "outDir": "dist", // 컴파일된 자바스크립트 폴뎌
    "strict": false, // 엄격한 검사
    "moduleDetection": "force", // 개별 모듈로 취급
    "moduleResolution": "node",
    "skipLibCheck": true,
  },
  "include": ["src"] // 폴더 안에 파일 컴파일
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 타입스크립트는 모든 파일을 전역 모듈로 본다 export{} 처럼 모듈 키워드를 쓰면 그때부터 독립된 모듈로 본다. 또는 tsconfig.json 파일에 moduleDetection : force 옵션을 준다.
npm install -D @types/node@20
1

# 타입스크립트 기본타입

# 원시타입

하나의 값만 저장하는 타입

: number = 타입 어노테이션

// number
let num1: number = 123;
let num2: number = -123;
let num3: number = 0.123;
let num4: number = -0.123;
let num5: number = Infinity;
let num6: number = -Infinity;
let num7: number = NaN;

// string
let str1: string = "hello";
let str2: string = "hello";
let str3: string = `hello`;
let str4: string = `hello ${num1}`;

// boolean
let bool1: boolean = true;
let bool2: boolean = false;

// null
let null1: null = null;

// undefined
let unde1: undefined = undefined;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

number 타입에 임시로 null 을 넣고 싶을때
null 타입이 아닌 변수에 null을 넣을 수 있다.

tsconfig.json

"strictNullChecks": false,
1

# 리터럴 타입

값 자체가 타입이 된다 타입으로 정의된 값 외에 다른 값은 들어갈 수 없다

// 리터럴 타입
// 리터럴 -> 값
let numA: 10 = 10;

let strA: "hello" = "hello";
1
2
3
4
5

# 배열

// 배열
let numArr: number[] = [1, 2, 3];

let strArr: string[] = ["hello", "im", "winterlood"];

let boolArr: Array<boolean> = [true, false, true]; // 제네릭

// 배열에 들어가는 요소들의 타입이 다양할 경우
let multiArr: (number | string)[] = [1, "hello"];

// 다차원 배열의 타입을 정의하는 방법
let doubleArr: number[][] = [
  [1, 2, 3],
  [4, 5],
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 튜플

자바스크립트에는 없고 타입스크립트에만 제공 길이와 타입이 고정된 배열

let tup1: [number, number] = [1, 2];

let tup2: [number, string, boolean] = [1, "2", true];

const users: [string, number][] = [
  ["이이이", 1],
  ["이아무개", 2],
  ["김아무개", 3],
  ["박아무개", 4],
  //   [5, "최아무개"],
];
1
2
3
4
5
6
7
8
9
10
11

# 객체

  • ? : 선택적 프로퍼티 optional property 있어도 되고 없어도 됨
  • readonly : 수정 불가
let user: {
  id?: number;
  name: string;
} = {
  id: 1,
  name: "이이이",
};

let config: {
  readonly apiKey: string;
} = {
  apiKey: "MY API KEY",
};

// config.apiKey = "hacked";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 타입 별칭 (type alias)

타입을 변수처럼

type User = {
  id: number;
  name: string;
  nickname: string;
  birth: string;
  bio: string;
  location: string;
};

let user: User = {
  id: 1,
  name: "이이이",
  nickname: "winterlood",
  birth: "1997.01.07",
  bio: "안녕하세요",
  location: "부천시",
};

function test() {
  type User = string; // 함수 안에서 User 타입은 string 이 된다
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 인덱스 시그니처

type CountryCodes = {
  [key: string]: string;
};

let countryCodes: CountryCodes = {
  Korea: "ko",
  UnitedState: "us",
  UnitedKingdom: "uk",
};

type CountryNumberCodes = {
  [key: string]: number;
  Korea: number; // Korea 프로퍼티가 반드시 있어야 한다
};

type CountryNumberCodes = {
  [key: string]: number;
  Korea: string; // 인덱스시그니처 타입과 반드시 일치해야 한다 오류발생
};

// let countryNumberAndStringCodes: CountryNumberCodes = {
//   Korea: "ko",
// };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 열거형 타입 (Enum Type)

타입스크립트에서만 제공

enum Role {
  ADMIN,
  USER,
  GUEST,
}

enum Language {
  korean = "ko",
  english = "en",
}

const user1 = {
  name: "이이이",
  role: Role.ADMIN, // 0 <- 관리자
  language: Language.korean,
};

const user2 = {
  name: "홍길동",
  role: Role.USER, // 1 <- 일반 유저
  language: Language.english,
};

const user3 = {
  name: "아무개",
  role: Role.GUEST, // 2 <- 게스트
};

console.log(user1, user2, user3);
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

# Any

특정 변수의 타입을 확실히 모를때

let anyVar: any = 10;

let num: number = 10;
num = anyVar; // 가능 unkown 은 불가능
1
2
3
4

# Unknown

let unknownVar: unknown;

unknownVar = "";
unknownVar = 1;
unknownVar = () => {};

if (typeof unknownVar === "number") {
  // 타입 정제
  num = unknownVar;
}
1
2
3
4
5
6
7
8
9
10

# void

function func1(): string {
  return "hello";
}

function func2(): void {
  console.log("hello");
}
1
2
3
4
5
6
7

# never

불가능한 타입 strictNullChecks : false 여도 값을 담을 수 없음. anytype 도 담을 수 없다 (void 타입은 담을 수 있다)

function func3(): never {
  while (true) {}
}

function func4(): never {
  throw new Error();
}

let anyVar: any;

let a: never;
// a = 1;
// a = {};
// a = "";
// a = undefined;
// a = null;
// a = anyVar;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 타입은 집합이다

** 밑에서 위로 변수 할당 가능 ** anytype은 다운캐스팅/업케스팅 무시 ** never 타입에는 anytype 들어가지 못한다

# 객체 타입의 호환성

type Animal = {
  name: string;
  color: string;
};

type Dog = {
  name: string;
  color: string;
  breed: string;
};

let animal: Animal = {
  name: "기린",
  color: "yellow",
};

let dog: Dog = {
  name: "돌돌이",
  color: "brown",
  breed: "진도",
};

animal = dog;
// dog = animal; 오류 발생
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

animal 은 dog의 슈퍼타입/ dog 는 animal 의 서브타입

# 초과 프로퍼티 검사

type Book = {
  name: string;
  price: number;
};

let book2: Book = {
  name: "한 입 크기로 잘라먹는 리액트",
  price: 33000,
  //   skill: "reactjs", skill 작성 불가
};
1
2
3
4
5
6
7
8
9
10

# 대수 타입

여러개의 타입을 합성해서 새롭게 만들어낸 타입

# union 타입 (합집합 타입)

let a: string | number | boolean;
a = 1;
a = "hello";
a = true;

let arr: (number | string | boolean)[] = [1, "hello", true];
1
2
3
4
5
6
type Dog = {
  name: string;
  color: string;
};

type Person = {
  name: string;
  language: string;
};

type Union1 = Dog | Person; // 타입 별칭으로도 union 타입을 만들 수 있다

let union1: Union1 = {
  // Dog
  name: "",
  color: "",
};

let union2: Union1 = {
  // Person
  name: "",
  language: "",
};

let union3: Union1 = {
  // Dog | Person
  name: "",
  color: "",
  language: "",
};

// 허용 안됨
// let union4: Union1 = {
//   name: "",
// };
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
32
33
34
35

# Intersection 타입 (교집합 타입)

let variable: number & string; // => never 타입 (불가능.. 공집합)
1

객체 타입에서 주로 많이 사용한다

type Dog = {
  name: string;
  color: string;
};

type Person = {
  name: string;
  language: string;
};

type Intersection = Dog & Person;

// 프로퍼티가 하나라도 빠지면 안된다 모든 프로퍼티를 포함하는 타입
let intersection1: Intersection = {
  name: "",
  color: "",
  language: "",
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 타입 추론 (Type Inference)

타입이 정의되어 있지 않은 변수의 타입을 자동으로 추론

# any type의 진화

let d; // any
d = 10; // any
d.toFixed(); // number

d = "hello"; // any
d.toUpperCase(); // string
1
2
3
4
5
6

변수를 선언하기만 하면 암묵적인 any타입으로 지정됨. 암묵적인 any type은 진화가능.

# const

const num = 10; // const 는 넘버리터럴타입으로 추론됨
const str = "hello";
1
2

# 최적의 공통타입으로 추론해준다

let arr = [1, "string"]; // let arr: (string | number)[]
1

# 타입 단언 (Type Assertion)

실제로 타입을 바꾸는것은 아니다. 컴파일러에게 잠시 단언해서 오류를 피함

타입 단언의 규칙

  • 값 as 단언 <- 단언식
  • A as B
  • A가 B의 슈퍼타입이거나
  • A가 B의 서브타입이어야 함
type Person = {
  name: string;
  age: number;
};

let person = {} as Person;
person.name = "이이이";
person.age = 27;
1
2
3
4
5
6
7
8
type Dog = {
  name: string;
  color: string;
};

let dog = {
  name: "돌돌이",
  color: "brown",
  breed: "진도",
} as Dog;
1
2
3
4
5
6
7
8
9
10
let num1 = 10 as never;
let num2 = 10 as unknown;

let num3 = 10 as unknown as string; // 다중 단언 좋은 방법은 아니다
1
2
3
4

# const 단언

readonly 로 추론됨. 모든 프로퍼티를 readonly로 만들어준다

let num4 = 10 as const;

let cat = {
  name: "야옹이", // readonly 로 추론됨
  color: "yellow", // readonly 로 추론됨
} as const;
1
2
3
4
5
6

# Non null 단언

type Post = {
  title: string;
  author?: string;
};

let post: Post = {
  title: "게시글1",
};

const len: number = post.author!.length; // ? => !
// ! : 이 값이 null 이거나 undefined이 아닐것이다 라고 단언
1
2
3
4
5
6
7
8
9
10
11

# 타입 좁히기

조건문 등을 이용해 넓은타입에서 좁은타입으로 타입을 상황에 따라 좁히는 방법

type Person = {
  name: string;
  age: number;
};

// value => number : toFixed
// value => string : toUpperCase
// value => Date : getTime
// value => Person : name은 age살 입니다.
function func(value: number | string | Date | null | Person) {
  // 여기서는 toFixed 나 toUpperCase 사용 불가
  // 타입 가드
  if (typeof value === "number") {
    console.log(value.toFixed());
  } else if (typeof value === "string") {
    console.log(value.toUpperCase());
  } else if (value instanceof Date) {
    console.log(value.getTime());
  } else if (value && "age" in value) {
    console.log(`${value.name}${value.age}살 입니다`);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 서로소 유니온 타입

type LoadingTask = {
  state: "LOADING";
};

type FailedTask = {
  state: "FAILED";
  error: {
    message: string;
  };
};

type SuccessTask = {
  state: "SUCCESS";
  response: {
    data: string;
  };
};

type AsyncTask = LoadingTask | FailedTask | SuccessTask;

function processResult(task: AsyncTask) {
  switch (task.state) {
    case "LOADING": {
      console.log("로딩 중");
      break;
    }
    case "FAILED": {
      console.log(`에러 발생 : ${task.error.message}`);
      break;
    }
    case "SUCCESS": {
      console.log(`성공 : ${task.response.data}`);
      break;
    }
  }
}
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
32
33
34
35
36

# 함수 타입

어떤 [타입의] 매개변수를 받고, 어떤 [타입의] 결과값을 반환하는지 함수를 설명

# 함수 선언식

function func(a: number, b: number) {
  return a + b; // number로 추론 된다
}
1
2
3

# 화살표 함수

const add = (a: number, b: number) => a + b;
1

# 함수의 매개변수

function introduce(name = "이이이", age: number, tall?: number) {
  console.log(`name : ${name}`);
  if (typeof tall === "number") {
    console.log(`tall : ${tall + 10}`);
  }
}

introduce("이이이", 27, 175);

introduce("이이이", 27);
1
2
3
4
5
6
7
8
9
10

# rest파라미터

function getSum(...rest: []) {
  let sum = 0;
  rest.forEach((it) => (sum += it));

  return sum;
}

function getSum(...rest: [number, number, number]) {
  let sum = 0;
  rest.forEach((it) => (sum += it));

  return sum;
}

getSum(1, 2, 3); // 6
// getSum(1, 2, 3, 4, 5); // 15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 함수 타입 표현식

type Operation = (a: number, b: number) => number;

const add: (a: number, b: number) => number = (a, b) => a + b;
const sub: Operation = (a, b) => a - b;
1
2
3
4

# 호출 시그니처 (콜 시그니쳐)

type Operation2 = {
  (a: number, b: number): number;
  name: string;
}; // 하이브리드타입

const add2: Operation2 = (a, b) => a + b;
const sub2: Operation2 = (a, b) => a - b;
const multiply2: Operation2 = (a, b) => a * b;
const divide2: Operation2 = (a, b) => a / b;

// add2();
// add2.name
1
2
3
4
5
6
7
8
9
10
11
12

# 함수타입의 호환성

특정 함수 타입을 다른 함수 타입으로 취급해도 괜찮은가

  1. 반환값의 타입이 호환되는가 : !!다운캐스팅은 불가능

  2. 매개변수의 타입이 호환되는가 : !!업캐스팅은 불가능

    2-1. 매개변수의 개수가 같을 때

    type C = (value: number) => void;
    type D = (value: 10) => void;
    
    let c: C = (value) => {};
    let d: D = (value) => {};
    
    // c = d;
    d = c;
    
    1
    2
    3
    4
    5
    6
    7
    8

    2-2. 매개변수의 개수가 다를 때

    type Func1 = (a: number, b: number) => void;
    type Func2 = (a: number) => void;
    
    let func1: Func1 = (a, b) => {};
    let func2: Func2 = (a) => {};
    
    func1 = func2;
    // func2 = func1;
    
    1
    2
    3
    4
    5
    6
    7
    8

# 함수 오버로딩

하나의 함수를 매개변수의 개수나 타입에 따라 여러가지 버전으로 만드는 문법. 타입스크립트에서만 지원

// 버전들 -> 오버로드 시그니쳐
function func(a: number): void;
function func(a: number, b: number, c: number): void;

// 실제 구현부 -> 구현 시그니쳐
function func(a: number, b?: number, c?: number) {
  if (typeof b === "number" && typeof c === "number") {
    console.log(a + b + c);
  } else {
    console.log(a * 20);
  }
}

func(1);
func(1, 2, 3);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 사용자 정의 타입 가드

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

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

type Animal = Dog | Cat;

function isDog(animal: Animal): animal is Dog {
  return (animal as Dog).isBark !== undefined;
}

function isCat(animal: Animal): animal is Cat {
  return (animal as Cat).isScratch !== undefined;
}

function warning(animal: Animal) {
  if (isDog(animal)) {
    // 강아지
    animal;
  } else if ("isScratch" in animal) {
    // 고양이
  }
}
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

# 인터페이스

호출 시그니처로 오버로드 구현 가능

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

const person: Person = {
  name: "이정환",
  sayHi: function () {
    console.log("Hi");
  },
};

person.sayHi();
person.sayHi(1, 2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 인터페이스 확장

type Animal = {
  name: string;
  color: string;
};

interface Dog extends Animal {
  isBark: boolean;
}

interface Cat extends Animal {
  isScratch: boolean;
}

const dog: Dog = {
  name: "", // 원본 타입의 서브타입 다시 정의 가능
  color: "",
  isBark: true,
};

interface DogCat extends Dog, Cat {}

const dogCat: DogCat = {
  name: "",
  color: "",
  isBark: true,
  isScratch: true,
};
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

# 선언 합침

interface Person {
  name: string;
}

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

interface Developer extends Person {
  name: "hello";
}

const person: Person = {
  name: "",
  age: 27,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 모듈 보강

선언 병합은 이미 정의된 타입(특히 라이브러리 타입)에 새로운 속성이나 메소드를 추가하여 타입 정의를 확장할 때 유용하게 사용됩니다. 이를 모듈 보강이라고 합니다.

interface Lib {
  a: number;
  b: number;
}

interface Lib {
  c: string;
}

const lib: Lib = {
  a: 1,
  b: 2,
  c: "hello",
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 자바스크립트 클래스

class Student {
  // 필드
  name;
  grade;
  age;

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

  // 메서드
  study() {
    console.log("열심히 공부 함");
  }

  introduce() {
    console.log(`안녕하세요 ${this.name} 입니다!`);
  }
}

class StudentDeveloper extends Student {
  // 필드
  favoriteSkill;

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

  // 메서드
  programming() {
    console.log(`${this.favoriteSkill}로 프로그래밍 함`);
  }
}
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
32
33
34
35
36
37
38

클래스를 이용해서 만든 객체 -> 인스턴스

스튜던트 인스턴스

let studentB = new Student("홍길동", "A+", 27);
console.log(studentB);
studentB.study();
studentB.introduce();
1
2
3
4

# 타입스크립트 클래스

class Employee {
  // 필드
  name: string;
  age: number;
  position: string;

  // 생성자
  constructor(name: string, age: number, position: string) {
    this.name = name;
    this.age = age;
    this.position = position;
  }

  // 메서드
  work() {
    console.log("일함");
  }
}

const employeeB = new Employee("이정환", 27, "개발자");
console.log(employeeB);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

자바스크립트의 클래스 이면서 하나의 타입

const employeeC: Employee = {
  name: "",
  age: 0,
  position: "",
  work() {},
};
1
2
3
4
5
6

# super

타입스크립트에서는 생략하면 오류발생

class ExecutiveOfficer extends Employee {
  // 필드
  officeNumber: number;

  // 생성자
  constructor(
    name: string,
    age: number,
    position: string,
    officeNumber: number
  ) {
    super(name, age, position); // 부모클래스의 생성자
    this.officeNumber = officeNumber;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 접근제어자(access modifier)

# public

접근 가능

# private

접근 불가

# proteced

파생클래스에서는 접근 가능

# 생성자에 접근제어자 붙이면 필드 자동생성됨 필드 생략 가능

class Employee {
  // 필드

  // 생성자
  constructor(
    private name: string,
    protected age: number,
    public position: string
  ) {}

  // 메서드
  work() {
    console.log(`${this.name} 일함`);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 인터페이스와 클래스

인터페이스를 이용해 클래스에 어떤 필드들이 존재하고, 어떤 메서드가 존재하는지 정의

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} 속도로 이동!`);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 제네릭

모든 타입의 값을 다 적용

// 제네릭 함수
function func<T>(value: T): T {
  return value;
}

let arr = func<[number, number, number]>([1, 2, 3]);
// 1. T에 [Number, Number, Number] 튜플 타입이 할당됨
// 2. 매개변수 value와 반환값 타입이 모두 튜플 타입이 됨
1
2
3
4
5
6
7
8

# Map 메서드 타입 정의

Map은 다음과 같이 원본 배열의 각 요소에 콜백함수를 수행하고 반환된 값들을 모아 새로운 배열로 만들어 반환

function map<T, U>(arr: T[], callback: (item: T) => U) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(callback(arr[i]));
  }
  return result;
}
1
2
3
4
5
6
7

# forEach

배열의 모든 요소에 콜백함수를 한번씩 수행

function forEach<T>(arr: T[], callback: (item: T) => void) {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i]);
  }
}
1
2
3
4
5

# 제네릭 인터페이스

interface KeyPair<K, V> {
  key: K;
  value: V;
}
1
2
3
4

# 인덱스 시그니쳐와 함께 사용

interface Map<V> {
  [key: string]: V;
}

let stringMap: Map<string> = {
  key: "value",
};

let booleanMap: Map<boolean> = {
  key: true,
};
1
2
3
4
5
6
7
8
9
10
11

# 제네릭 타입 별칭

type Map2<V> = {
  [key: string]: V;
};

let stringMap2: Map2<string> = {
  key: "string",
};
1
2
3
4
5
6
7

# 제네릭 클래스

class List<T> {
  constructor(private list: T[]) {}

  push(data: T) {
    this.list.push(data);
  }

  pop() {
    return this.list.pop();
  }

  print() {
    console.log(this.list);
  }
}

const numberList = new List<number>([1, 2, 3]);
numberList.pop();
numberList.push(4);
numberList.print();

const stringList = new List<string>(["1", "2"]);
stringList.push("hello");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 프로미스

const promise = new Promise<number>((resolve, reject) => {
  setTimeout(() => {
    // resolve(20);
    reject("~~ 때문에 실패");
  }, 3000);
});

promise.then((response) => {
  console.log(response * 10); // 20
});

promise.catch((err) => {
  if (typeof err === "string") {
    console.log(err);
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 타입조작

  • 인덱스드 엑세스 타입은 인덱스를 이용해 다른 타입내의 특정 프로퍼티의 타입을 추출하는 타입
  • keyof 연산자는 객체 타입으로부터 프로퍼티의 모든 key들을 String Literal Union 타입으로 추출하는 연산자
  • typeof 연산자는 자바스크립트에서 특정 값의 타입을 문자열로 반환하는 연산자. 타입을 정의할 때 사용하면 특정 변수의 타입을 추론하는 기능도 가지고 있습니다.
  • 맵드 타입은 기존의 객체 타입을 기반으로 새로운 객체 타입을 만드는 마법같은 타입 조작 기능
  • 템플릿 리터럴 타입 가장 단순한 기능으로 템플릿 리터럴을 이용해 특정 패턴을 갖는 String 타입을 만드는 기능

# 조건부 타입

extends와 삼항 연산자를 이용해 조건에 따라 각각 다른 타입을 정의하도록 돕는 문법

# 제네릭 조건부 타입

type StringNumberSwitch<T> = T extends number ? string : number;

let varA: StringNumberSwitch<number>;
// string

let varB: StringNumberSwitch<string>;
// number
1
2
3
4
5
6
7

# 분산적인 조건부 타입

조건부 타입의 타입 변수에 Union 타입을 할당하면 분산적인 조건부 타입으로 조건부 타입이 업그레이드 되기 때문입니다.

type StringNumberSwitch<T> = T extends number ? string : number;

(...)

let c: StringNumberSwitch<number | string>;
// string | number
1
2
3
4
5
6

# infer

조건부 타입 내에서 특정 타입을 추론

type FuncA = () => string;

type FuncB = () => number;

type ReturnType<T> = T extends () => infer R ? R : never;

type A = ReturnType<FuncA>;

type B = ReturnType<FuncB>;

type C = ReturnType<number>;
1
2
3
4
5
6
7
8
9
10
11

타입 변수 T에 함수 타입 FuncA가 할당됩니다. T는 () ⇒ string 이 됩니다. 조건부 타입의 조건식은 다음 형태가 됩니다 () ⇒ string extends () ⇒ infer R ? R : never 조건식을 참으로 만드는 R 타입을 추론 합니다 그 결과 R은 string이 됩니다. 추론이 가능하면 이 조건식을 참으로 판단합니다 따라서 결과는 string이 됩니다. 불가능하다면 조건식을 거짓으로 판단 never

# 유틸리티 타입

타입스크립트가 자체적으로 제공하는 특수한 타입들입니다. 우리가 지금까지 배웠던 제네릭, 맵드 타입, 조건부 타입 등의 타입 조작 기능을 이용해 실무에서 자주 사용되는 유용한 타입들을 모아 놓은 것을 의미

# Partial

특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환

# Required

특정 객체 타입의 모든 프로퍼티를 필수(선택적이지 않은) 프로퍼티로 변환

# Readonly

특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 변환

# Pick<T, K>

특정 객체 타입으로부터 특정 프로퍼티 만을 골라내는 그런 타입

# Omit<T, K>

특정 객체 타입으로부터 특정 프로퍼티 만을 제거하는 타입

# Exclude<T, K>

다음과 같이 T로부터 K를 제거하는 타입

# Extract<T, K>

T로 부터 K를 추출하는 타입

# ReturnType

T에 할당된 함수 타입의 반환값 타입을 추출하는 타입