본문 바로가기

Develop_story/TIL(Today I Learned)

TypeScript

SMALL

1. type[ ] => 지정한 type이 들어가는 배열

let a: number[] = [1, 2]; // typescript가 자동으로 type 추론
let b: string[] = ["li", "1"];
let c: boolean[] = [true];
  • typescript가 자동으로 type을 추론해주기 때문에 이런식으로 작성하지 않아도 괜찮음
let c = [true, false];

2. optional

객체에서 key를 선택적으로 가지고 있을 때

// name은 항상 가지고 있고 age는 선택적으로 가지고 있을 때
const player: { name: string; age?: number } = {
  name: "nico",
};
  • age가 undefined이거나 number ( age?: number | undefined )

3. alias type

type Age = number;

type Player = {
  name: string;
  age?: Age;
};
const nico: Player = {
  name: "nico",
};
const lynn: Player = {
  name: "lynn",
  age: 12,
};

4. 함수의 return 값

함수가 return하는 type이 무엇인지 알 수 있으면 더 많은 보호장치를 가질 수 있음

function playerMaker(name: string) {
  return {
    name,
  };
}
const uk = playerMaker("nico");
uk.age = 12;		// age는 playerMaker 안에 age 라는 type을 지정하지 않았기 때문에 error 발생
function playerMaker(name: string): Player {
  return {
    name,
  };
}
  • type은( argument ) 뒤에 지정해주면 됨

화살표 함수의 경우

const playerMaker = (name: string): Player => ({name})

5. readonly

읽기 전용으로 지정

type Player = {
  readonly name: string;
  age?: Age;
};

nico.name = "lala" 		// name은 읽기 전용이기 때문에 변경할 수 없음
  • name을 readonly로 읽기 전용으로 지정했기 때문에 변경할 수 없음
const numbers: readonly number[] = [1, 2, 3, 4]
numbers.push(1)

const names: readonly string[] = ["1", "2"]
names.push("3")
  • 이전 코드와 마찬가지로 읽기 전용이기 때문에 push나 배열을 바꾸는 메서드를 사용할 수 없음
  • 단, map이나 filter처럼 배열에 변화를 주지 않는 메서드는 사용 가능

6. Tuple

// platers에 빨간 줄 (error)
const players: [string, number, boolean]= []
  • players  배열 안에 값이 3개가 필요함
  • 배열 안에 값의 순서는 string, number, boolean
const players: [string, number, boolean]= ['nico', 12, false]

players[0] = 1 		// error 발생
  • players의 첫 번째 인자는 string이어야 하기 때문에 error 발생
const players: readonly [string, number, boolean]= ['nico', 12, false]
  • readonly로 지정할 수 있음

7. unknown

어떤 타입인지 모르는 변수 지정 => API로 응답을 받는데 어떤 값인지 모르는 경우

let a: unknown;
let b = a + 1;		// error 발생
  • a 의 type이 무엇인지 정해지지 않음
if(typeof a === 'number') { // 범위 내에서 d는 number
    let b = a + 1
}

if(typeof a === 'string') {
    let b = a.toUpperCase();
}
  • a가 number인 경우 b는 a + 1
  • a가 string인 경우 b는 a를 toUpperCase 메서드 사용

8. void

return하지 않는 함수에 사용

function hello(): void {
    console.log('x')
}

9. never

 1) 함수가 절대로 return하지 않는 경우 사용

function hi():never  {
    return "X"
}
  • never은 함수가 절대 return하지 않는 경우이기 때문에 return "X" 부분에 error 발생
function hi():never  {
    throw new Error("XXX")
}
  • 위의 함수는 return을 하지 않고 오류를 발생시키는 함수

 2) 타입이 두가지 일 수도 있는 상황에서 발생

// never
function hi(title: string | number) {
  if (typeof title === "string") {
    title;      // string
  } else if (typeof title === "number") {
    title;      // number
  } else {
    // title은 string 혹은 number를 받기 때문에 이 부분의 title은 절대 작동할 일이 없음
    title;		// never
  }
}
  • title은 string 혹은 number를 받는다
  • 위의 if (typeof title === "string")과 else if (typeof title === "number") 부분이 있음
  • 따라서 마지막 부분인 else 안에 있는 title은 절대 작동할 일이 없어 title의 type은 never로 지정됨

10. Call Signature

내가 원하는 나만의 함수 타입을 만드는 방법

우리가 타입스트립트에게 이 함수가 어떻게 호출되는지 설명해주는 방법

type Add = (a: number, b: number) => number;
const add: Add = (a, b) => a + b;
  • type Add에서 a와 b가 number이고 반환하는 값이 number라고 미리 지정
  • 따라서 add 함수에 있는 인자의 type을 지정하지 않고 a + b 는 number를 return해야 하기 때문에 void가 올 수 없음

11. Overloading

함수가 서로 다른 여러 개의 call signatures를 가지고 있는 경우 발생

보통 외부 라이브러리를 사용할 때 발생하게 됨

 1) 개념

type Add2 = {
    (a: number, b: number) : number
    (a: number, b: string) : number
}

// b가 number가 될 수 있고 string도 될 수 있음
const add2 : Add2 = (a, b) => a + b
  • b가 number도 될 수 있고 string도 될 수 있기 때문에 error 발생
const add2 : Add2 = (a, b) => {
    if(typeof b === "string") return a
    return a + b
}
  • b가 string인 경우 a만 반환
  • b가 string이 아닌 경우 a+b 반환

 2) 라이브러리 예시

type Config = {
  path: string;
  state: object;
};

type Push = {
  (path: string): void;
  (config: Config): void;
};

const push: Push = (config) => {
  if (typeof config === "string") {
    console.log(config);
  } else {
    console.log(config.path);
  }
};

 3) 파라미터의 갯수가 다른 경우

type Add3 = {
  (a: number, b: number): number;
  (a: number, b: number, c: number): number;
};
const add3: Add3 = (a, b, c?: number) => {
  if (c) return a + b + c;
  return a + b;
};
  • 인자가 많은 부분은 optional로 지정

12. Polymorphism with generic

다양성을 의미한다.

type SuperPrint = {
    (arr: number[]): void
    (arr: boolean[]): void
    (arr: string[]): void
}

const superPrint: SuperPrint = (arr) => {
    arr.forEach(i => console.log(i))
}

superPrint([1, 2, 3, 4])
superPrint([true, false, true])
superPrint(["a", "b"])
  • 여기서 number, string, boolean, unknown, void 등은 concrete type
  • 우리는 concrete type 대신 placeholder를 작성할 수 있음
  • 문제점: call signature 가 3개나 있음
superPrint([1, 2, true, false, "a", "b"])

위와 같이 코드를 작성하면 error 발생

=> 어떤 타입이든 동작하게 하고 싶은 경우

Generic 사용

Generic을 사용하는 이유

  • call signature를 작성할 때, 들어올 확실한 타입을 모를 때 generic 사용
    1. 타입스크립트에 generic을 사용하고싶다고 알림
    2. generic 이름은 원하는대로 작성 (대개 <T, V>, <TypePlaceholder>)
type SuperPrint = {
    <TypePlaceholder>(arr: TypePlaceholder[]): void
}

const superPrint: SuperPrint = (arr) => arr[0]

superPrint([1, 2, true, false, "a", "b"])

  • 위와 같이 작성한 경우 generic이 알아서 Placeholder 대신 타입스크립트가 발견한 type으로 바꿔줌
  • any와 같지 않고 우리가 하는 요청에 따라 call signature를 생성
type SuperPrint = {
  <T, V>(arr: T[], b: V): T;
};

const superPrint: SuperPrint = (arr) => arr[0]

const q = superPrint([1, 2, 3, 4], "X");
  • 위와 같이 인자가 여러개인 경우도 사용 가능
function superPrint2<W>(t: W[]) {
  return t[0];
}

const t = superPrint2<boolean>([1, 2, 3, 4]);	// 배열 안의 값들 error
  • W를 boolean으로 지정했기 때문에 1, 2, 3, 4는 number임으로 error 발생함
  • => generic이 추론을 해주기 때문에 <number>로 지정할 필요 없음
const [title, setTitle] = useState<number>(0)
  • useState를 사용할 때, number 혹은 string 등 타입을 지정하면 number(string) tpye의 useState가 됨 

13. interface

type과 같이 오브젝트의 모양을 결정하는 역할을 하지만 type은 interface 보다 조금 더 활용하는 방법이 많음

interface Hello = string
  • 위와 같은 방법을 사용할 수 없음
  • => interface는 오로지 오브젝트의 모양을 타입스크립트에 설명해주는 역할만 함
// type에 특정 값을 갖게 하기
type Team = "red" | "blue" | "yellow"
type Health = 1 | 5 | 10

interface Gamer {
  nickname: string
  team: Team
  health: Health
}
const uk: Gamer = {
  nickname: "ukTheBlood",
  team: "red",	// "red", "blue", "yellow"
  health: 10	// 1 | 5 | 10
}
  • 위와 같이 string 같은 타입 뿐만이 아니라 오게 되는 이름까지 정할 수 있음

1) 상속

interface User {
  name: string;
}
interface Login extends User {
  
}
const ukBlood: Login = {
  name: "uk",
};
  • 위와 같이 상속 기능을 사용할 수 있음

2) merging

interface User {
  name: string;
}
interface Login extends User {
  age: number;
}
const ukBlood: Login = {
  name: "uk",
  age: 25,
};

 

LIST