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;
이렇게 특정 값 자체를 타입으로 설정해줄 수 있음, 해당 값 이외의 값을 할당해주면 에러
동일한 속성을 가진 집합
구조적 타입시스템
조건이 더 적은 객체가 슈퍼타입이 될 수 있음
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: "",
};
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
A 가 B의 슈퍼타입이거나
A 가 B의 서브타입이어야 함
→ 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 타입으로 각각 선언해주면 교집합이 될 수 없게 되어 서로소 리유니온 타입이 된다.
npm init -y
자동으로 옵션 스루해줌
함수 설명하는 가장 좋은 방법
→ 어떤 타입의 매개변수를 받고, 어떤 타입의 결과값을 반환하는지
선택적 매개변수는 필수 매개변수 앞에 올 수 없다.
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 라는 배열에 넣어줌
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;
⇒ 함수 반환값 타입 별칭
→ 특정 함수 타입을 다른 함수 타입으로 취급해도 괜찮은가 판단.
type A = () => number;
type B = () => 10;
let a: A = () => 10; //<= number 타입
let b: B = () => 10; //<= 넘버 리터럴 타입
a = b; // ✅ 업캐스팅 (호환)
//b = a; // ❌ 다운캐스팅
→ 반환값은 업캐스팅 가능, 다운캐스팅 불가
→ 넘버 리터럴 타입이 더 한정돼있어서 넘버타입 호환 안됨.
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개인 쪽은 타입 두개가 같아야 함.
함수를 매개변수의 개수나 타입에 따라 여러가지 버전으로 정의하는 방법
하나의 함수 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);
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 ? "할큅니다" : "안할퀴어요");
}
}