타입스크립트 개념

가연·2023년 9월 1일

tsconfig 초기 설정

npm init(nodejs 초기화)

npm i @types/node : 노드자스의 타입 정보를 가지고 있는 모듈 섪치

sudo npm i typescript -g : 타스 컨파일러 설치, 글로벌옵션으로-컴퓨터 전체에 설치

tsc -v : 설치 됐는지 확인

tsc 파일경로(index.ts) : 해당 파일 컴파일한 결과 나옴

node 파일경로(index.js) : 컴파일한 자스파일 실행

→ 번거로움

sudo npm install ts-node -g 설치 후

ts-node 파일경로(index.ts): 바로 실행결과 나옴


tsc --init : tsc 초기화

타입스크립트에서는 모든 타스 파일을 전역모듈로 봄.

→ 다른 파일에 있는 변수 이름과 겹치면 안됨..

→ 어떻게 해결??

아래에 export {} 를 써주면 이 파일을 독립된 모듈로 봄.

혹은, config에 모듈디텍션 추가!

"module": "ESNext" 상태에서 ts-node 사용하면 commonjs 문법 찾을 수 없어 에러 뜸.

package.json 에 "type": "module", 추가

이러면 또 타스 파일 자체를 이해 못하는 에러 뜸

"ts-node": {
    "esm": true
  },

추가해주기

{
  "compilerOptions": {
    "target": "ESNext",
    //ESNext 는 자스최신버전
    //구형 브라우저 에서 옛날 자스 문법 사용해야 할 때 필요
    "module": "ESNext",
    //프로젝트 환경에 따라 esnext 안되면 commonjs 사용하는 등 바꿀 수 있음
    "outDir": "dist",
    // 컴파일 된 자스 파일들 어디에 생성하면 좋을지
    "strict": true,
    //타입 검사를 엄격하게 할건지 아닌지(마이그레이션 할 떈 보통 false로 함. 너무 엄격하게 하면 다 에러로 떠서 불편)
    "moduleDetection": "force"
    //컴파일 시 export {} 자동 추가
    //각 타스 파일들 개별 모듈로 취급됨.
  },
  "ts-node": {
    "esm": true
  }, //"module": "ESNext" 에서도 ts-node 할 수 있게 추가해주기

  //컴파일된 파일들의 버전 설정
  "include": ["src"]
  //src 폴더 안의 파일들 모두 컴파일
  //tsc 만 입력해도 모두 컴파일됨.
}

원시타입

undefined, null, string, number, 리터럴 타입

원래 let num : number= null; 이런식으로 다른 타입에 널 못넣는데

컨피그에 "strictNullChecks": false 써주면 에러안남.

let ss: 11 = 11;
ss = 13;

이렇게 특정 값 자체를 타입으로 설정해줄 수 있음, 해당 값 이외의 값을 할당해주면 에러


Ch3

1.타입은 집합이다

동일한 속성을 가진 집합

객체 타입 호환성

구조적 타입시스템

조건이 더 적은 객체가 슈퍼타입이 될 수 있음


대수타입

1. 합집합- union 타입

https://ts.winterlood.com/44460889-64bd-4d4d-83af-9983f598fd2d

// 합집합 타입 - Union 타입
let a: string | number;

a = 1;
a = "hello";

둘 다 되는거

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

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

type Union1 = Dog | Person;
(...)

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

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

let union3: Union1 = { // ✅
  name: "",
  color: "",
  language: "",
};
-------------이렇게 모두 포함…. -----------------
let union4: Union1 = { // ❌
  name: "",
};

2. 교집합타입(Intersection)

let variable: number & string;

→ 교집합이 없으므로 never 타입으로 추론

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

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

type Intersection = Dog & Person;

let intersection1: Intersection = {
  name: "",
  color: "",
  language: "",
};
//이렇게 모든 프로퍼티가 들어있어야 가능. Dog와 Person 모두 동시에 만족해야함.

타입 추론

웬만하면 알아서 잘 추론해줌(초기값 기준으로 )

/**any 타입의 진화, 타입 넓히기?
 * 특정 타입 값 넣는 순간 그 타입으로 진화하는것
 */
let d;
d = 10; //대입한 값으로 추론 -> number 타입 됨.
d.toFixed;
//d.upperCase 에러

d = "sss"; //-> string 타입으로 진화

const k = 10; //넘버 리터럴 타입 10으로 추론됨.(const 는 값 변화 X)

let arr = [1, "string"];
// number|string => union 타입

타입 단언

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

let person = {} as Person;
//그냥 {} 로 하면 name, age 속성이 없다고 뜸 -> as 로 초기화 값 단언해줌.
person.name = "sss";
person.age = 12;

let person2 = {
  name: "ffff",
  age: 44,
  breed: "진도",
} as Person;
타입 단언의 규칙
값 as 단언 <- 단언식
A as B
AB의 슈퍼타입이거나
AB의 서브타입이어야 함

→ 10 as unknown as string 처럼 다중 단언 하면 가능하긴 하는데 별로 좋은 방법이 아님…..

/** const 단언
 */

let cat = {
  name: "ddd",
  color: "www",
} as const;

뒤에 as const 만 붙여주면 readonly 알아서 됨. -> 객채를 readonly 로 만들어야 할 때 편리함.

/**
 * Non Null 단언
 */
type Post = {
  title: string;
  author?: string;
};
let post: Post = {
  title: "ssd",
  author: "sddsdsds",
};
const len: number = post.author?.length;  ->O
const len: number = post.author!.length;  ->X

옵셔널 체이닝ㅡ 때문에 ? 써주면 이 값 자체가 undefined 가 될 수도 있기 때문에

에러남.

! ⇒ 이 값이 null 이거나 undefind가 아닐거라고 믿도록 만들게 단언한것.

타입 좁히기

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

타입 가드

해당 조건문 안에는 그 타입으로 좁혀지게 됨.(그 타입만 들어갈수있음.)

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

function func(value: number | string | Date | null | Person) {
  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());

// instanceof 란 value 가 Date 의 인스턴스 인가? -> null 통과 못함
//typeof 하면 null 값도 그대로 가기 때문에 value.getTime 사용 못함.
//instanceof 우측에 타입이 들어가면 안됨.(형식만 참조한다는 타입이라는 뜻)
//왼쪽의 값이 오른쪽 클래스의 인스턴스인가 라는 뜻
//따라서 우리가 만든 타입별칭에는 사용불가

  } else if (value && "age" in value) {

//value가 있을때 ..사용하고싶어서 value가 있을댸만&& age가 value 에 있냐 로
    console.log(`${value.name}${value.age}살 입니다`)
  }
}

서로소 유니온 타입

type Admin = {
  name: string;
  kickCount: number;
};

type Member = {
  name: string;
  point: number;
};

type Guest = {
  name: string;
  visitCount: number;
};

type User = Admin | Member | Guest;

function login(user: User) {
  if ("kickCount" in user) {
    // Admin
    console.log(`${user.name}님 현재까지 ${user.kickCount}명 추방했습니다`);
  } else if ("point" in user) {
    // Member
    console.log(`${user.name}님 현재까지 ${user.point}모았습니다`);
  } else {
    // Guest
    console.log(`${user.name}님 현재까지 ${user.visitCount}번 오셨습니다`);
  }
}

//이러면 직관적이지 않음

이거를 서로소 유니온 타입으로 변경하면 더 직관적으로 코드를 짤 수 있다.

type Admin = {
  tag: "ADMIN";
  name: string;
  kickCount: number;
};

type Member = {
  tag: "MEMBER";
  name: string;
  point: number;
};

type Guest = {
  tag: "GUEST";
  name: string;
  visitCount: number;
};
type User = Admin | Member | Guest;

function login(user: User) {
  switch (user.tag) {
    case "ADMIN": {
      console.log(`${user.name}님 현재까지 ${user.kickCount}명 추방했습니다`);
      break;
    }
    case "MEMBER": {
      console.log(`${user.name}님 현재까지 ${user.point}모았습니다`);
      break;
    }
    case "GUEST": {
      console.log(`${user.name}님 현재까지 ${user.visitCount}번 오셨습니다`);
      break;
    }
  }
}

→ tag : “MEMBER” 이런식으로 String Literal 타입으로 각각 선언해주면 교집합이 될 수 없게 되어 서로소 리유니온 타입이 된다.


section4

npm init -y

자동으로 옵션 스루해줌

ch0. 함수 타입 정의

함수 설명하는 가장 좋은 방법

→ 어떤 타입의 매개변수를 받고, 어떤 타입의 결과값을 반환하는지

화살표 함수 타입 정의

함수의 매개변수

선택적 매개변수는 필수 매개변수 앞에 올 수 없다.

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

→ age 의 위치를 tall 앞으로 바꿔야 함

function getSum(...rest: number[]) {
    //만약 인수 개수 고정하고 싶으면 튜플타입으로 [number,number,number] 이렇게 해주기!
  let sum = 0;
  rest.forEach((it) => (sum += it));
}
//rest 매개변수 : 할당된 여러 매개변수들을 rest 라는 배열에 넣어줌

Ch1 . 함수 타입 표현식

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

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

//매개변수를 타입 별칭을 이용해 중복 없이 사용할 수 있게 해주는 것

⇒ 함수의 매개변수 타입 별칭

호출 시그니처

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

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

⇒ 함수 반환값 타입 별칭

Ch2. 함수 타입의 호환성

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

1. 반환값이 호환되는가?

type A = () => number;
type B = () => 10;

let a: A = () => 10; //<= number 타입
let b: B = () => 10; //<= 넘버 리터럴 타입

a = b; // ✅ 업캐스팅 (호환)
//b = a; // ❌ 다운캐스팅

→ 반환값은 업캐스팅 가능, 다운캐스팅 불가

→ 넘버 리터럴 타입이 더 한정돼있어서 넘버타입 호환 안됨.

2. 매개변수 타입이 호환되는가

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

type C = (value: number) => void;
type D = (value: 10) => void;

let c: C = (value) => {};  // <= number 타입
let d: D = (value) => {};  // <= 넘버 리터럴 타입

//c = d; // ❌  <= 다운캐스팅
d = c; // ✅    <= 업캐스팅

→ 매개변수는 업캐스팅 불가, 다운캐스팅 가능

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

슈퍼타입 Animal , 서브타입 Dog 로 생각하면,

Dog 는 슈퍼타입이 가지고 있는 모든 매개변수를 가지고 있기 때문에 서브타입 = 슈퍼타입 인 다운캐스팅은 가능하지만, 슈퍼타입 = 서브타입 인 업캐스팅은 불가하다.

사실 이해가 잘 안됨..

이렇게 Animal, Dog 타입으로 하면 괜찮은데 넘버 타입이랑 넘버 리터럴 타입으로 하면 이해가 안됨.

넘버 리터럴 타입 = 넘버타입 하면 가능 , 즉 10 인 넘버 리터럴 타입에 다른 49 , 29 이런거 넣으면 안되지 않나..? 뭐지

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; // ❌

매개변수 2개 = 매개변수 1개 는 가능 , 반대는 불가

이때 매개변수 2개인 쪽은 타입 두개가 같아야 함.

Ch3. 함수 오버로딩

함수를 매개변수의 개수나 타입에 따라 여러가지 버전으로 정의하는 방법

하나의 함수 func
 * -> 모든 매개변수 타입 number
 * -> ver1. 매개변수 1 => 이 매개변수에 20을 곱한 값 출력
 * -> ver2. 매개변수 3 => 이 매개변수를 다 더한 값을 출력
function func(a: number): void;  //1
function func(a: number, b: number, c: number): void;  //2

//실제 구현부 -> 구현 시그니쳐
function func(a: number, b?: number, c?: number) {//이렇게 필수매개변수로 이용해버리면 1번 오버로드시그니쳐에서 에러가 나기 때문에 ? 붙여주기!
  a.toFixed;
}

// func(); // 오버로드 시그니쳐에 없어서 에러
func(1); // 오버로드 시그니쳐에 있으면 허용
func(1, 2, 3);

Ch4. 사용자 정의 타입가드

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 ? "할큅니다" : "안할퀴어요");
  }
}

//in 연산자로 타입을 좁히면 좋지 않음(프로퍼티 이름이 중간에 수정되면 타입 가드가 제대로 작동하지않고,
// 가독성이 떨어짐.)

//해결법!!!!!!! as 사용하기!!!!!!1

// 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 ? "할큅니다" : "안할퀴어요");
  }
}

0개의 댓글