
지금 하고 있는 자바스크립트에서는 잘못된 오류가 있다고 하면 실제로 동작을 할 때 브라우저의 console 에서 에러를 확인할 수 있다. 다시 생각해보면 실제로 동작을 시키기 전에는 코드가 잘못된 것인지 확인할 수가 없다..
📌 타입스크립트를 사용하면 코드를 작성하는 단계에서 잘못된 코드를 확인할 수 있다!!! 하지만 브라우저나 Node.js 환경에서 동작할 수 있는 코드가 아니기 때문에 자바스크립트로 변환 후 사용한다.
타입스크립트는 자바스크립트와 완벽 호환하며 대부분의 플러그인, 라이브러리, 프레임워크 또한 타입스크립트를 지원한다.
npm init -y 입력을 통해 package.json 파일 생성npm i -D typescript (개발의존성 패키지){
"compilerOptions": {
"strict": true //엄격 모드로 하겠다
},
"include": [
// src라는 폴더에 모든 하위경로에 .ts 확장자인 모든파일
"src/**/*.ts" //타입스크립트 코드를 제공할 경로
]
}
package.json 파일 scripts 부분에
"scripts": { "tsc": "tsc src/main.ts" },이 부분을 추가한 후 터미널에
npm run tsc를 입력하면 src/main.ts가 자바스크립트 파일로 변환된다.
tsconfig.json파일에 자바스크립트로 변환될 때 자바스크립트의 몇 버전으로 적용이 될 껀지target으로 지정해줄 수 있다.{ "compilerOptions": { "strict": true, "target": "ES2015" "lib": ["ESNext","DOM","DOM.Iterable"] }, "include": ["./src/**/*.ts"] }
lib으로는 타입스크립트에서 사용하려고 하는 라이브러리를 설정해줄 수 있다. ex) ESNext : 최신 버전, DOM, DOM.Iterable : 반복 가능한 객체를 사용할 때
// 배열
const week = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
console.log(week[0]) // Sun
console.log(week[6]) //Sat
console.log(week.findIndex(item => item === 'Sun')) //0
console.log(week.findIndex(item => item === 'Sat')) //6
//Enum 배열이나 객체스럽게 접근 가능
enum Week {Sun, Mon, Tue, Wed, Thu, Fri, Sat}
console.log(Week[0]) //Sun
console.log(Week[6]) //Sat
console.log(Week.Sun) //0
console.log(Week.Sat) //6
//자바스크립트로 변환된다면 이렇게 될 것이다.
//역방향 매핑
const EnumWeek = {
0: 'Sun', 1: 'Mon', 2: 'Tue', 3: 'Wed', 4: 'Thu', 5: 'Fri', 6: 'Sat',
Sun : 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6
}
console.log(EnumWeek[0]) //'Sun'
console.log(EnumWeek[6]) //'Sat'
console.log(EnumWeek.Sun) //0
console.log(EnumWeek.Sat) //6
enum Colors {Red, Green = 4, Blue}
console.log(Colors.Red) //0
console.log(Colors[0]) //Red
// Red는 앞에 있고 따로 건들지 않았기 때문에 0을 가짐
console.log(Colors.Red) //0
console.log(Colors.Green) //4
console.log(Colors.Blue) //5
나열하는 개념이기 때문에 뒤에 있는 Blue는 인덱스가 5가 된다. 예를 들면 4부터 다시 시작한다고 생각하자!
enum Colors {Red = 'r', Green = 4, Blue}
console.log(Colors.Red) //'r'
console.log(Colors[4]) // Green
console.log(Colors.r) // 역방향 매핑 불가
반환 타입const hello: (msg: string) => undefined = (msg) => {
console.log(`Hello ${msg}`);
};
const hello: (msg: string) => void = (msg) => {
console.log(`Hello ${msg}`);
};
hello('World!');
위처럼 함수는 undefined를 반환할 것이지만 반환하는 타입이 없으므로 void로 선언해 주어야 한다.
배열, 안쪽에 선언되어 있으면 튜플 타입이다.// 배열
const tup: number[] = [4,5]
tup[2] = 6 //[4,5,6]
tup[3] = 7 //[4,5,6,7]
// 튜플 타입
const tup: [number,number] = [4,5]
tup[2] = 6 //[4,5,6] 길이가 달라지기 때문에 에러 발생
tup[3] = 7 //[4,5,6,7] 에러 발생
const tup: [number, number] = [4,5]
tup[2] = 6; //error
tup.push(6) // [4,5,6]
tup.splice(2,0,6) //[4,5,6]
// [id, username, isValid]
const userA: [number, string, boolean] = [1, 'abc', true]
user[3] = 'abc@naver.com'; //error
userA.push('abc@naver.com')
위 코드에서 push와 splice코드를 사용해서 튜플 타입에 값을 넣었을 때는 길이가 변했음에도 오류가 발생하지 않는다... 이러한 점에 꼭 주의해서 사용하도록 하자!!
const nev: [] = []; // 아래 코드와 동일하게 동작
const nev: never[] = [];
nev.push(6) //할당 불가 Error
throw는 정상적으로 종료되지 않는 함수이다. 반환하는 타입도 없다. -> never 타입이 됨const myError: (error: string) => never = (error) => {
throw `에러임! - ${error}`;
};
try {
myError('Never 타입입니다.'); // '에러임! - Never 타입입니다.'
} catch (e) {
console.log(e);
}
let anything: any = 'Hello';
anything = 123;
anything = { A: 'a' };
anything = ['a', 'b', 'c'];
// 어떤 타입이든 상관없기 때문에..
const a: string = anything;
const b: number = anything;
const c: boolean = anything;
unknown 타입의 사용을 권장한다.// 어떤 것도 할당이 가능하기 때문에 unknown 타입의 변수에는 무엇이든 할당될 수 있음
let anything: unknown = 'Hello';
anything = 123;
anything = { A: 'a' };
anything = ['a', 'b', 'c'];
//타입가드
if(typeof anything === 'string'){
const d: string = anything; // 이건 가능 any보단 unknown을 권장
}
// 다른 타입에는 할당 불가
const a: string = anything; //Error
const b: number = anything; //Error
const c: boolean = anything; //Error
let any: any = 'Hello';
console.log(any.toUpperCase()); // 가능
any = 123;
console.log(any.toUpperCase()); // 가능 -> toUpperCase()는 문자열에서만 가능하므로 런타임에서는 에러 발생
오류가 있는 코드가 코드 상에서 오류가 없이 런타임으로 넘어가버린다.
let unknown: unknown = 'hi';
if (typeof unknown === 'string') {
console.log(unknown.toUpperCase()); // 이건 가능
}
console.log(unknown.toUpperCase()); //Error!
unknown = 123;
// 숫자데이터는 toUpperCase라는 메소드를 사용하지 못한다.
//코드 상에서 에러 확인 가능
if (typeof unknown === 'number') {
console.log(unknown.toUpperCase());
}
console.log(unknown.toUpperCase()); //Error! 코드 상에서 확인 가능
unknown 사용 시 타입을 정확하게 알 수 있는 경우에서만 활용이 가능해진다.
let uni: string | number | number[];
uni = 'Hello';
uni = 123;
uni = false; // Error
uni = null; // Error
uni = [1, 2, 3];
union 타입과 반대되는 개념으로 2개 이상의 타입이 허용되는게 아니라 병합해서 하나의 타입으로 만들어 논 것을 의미한다.type UserA = {
name: string;
age: number;
};
type UserB = {
isValid: boolean;
};
const user1: UserA = {
name: 'A',
age: 30,
isValid: true, //Error! UserA 타입에는 isValid 속성은 없다
};
const user2: UserB = {
name: 'B', //Error! UserB 타입에는 name속성은 없다
age: 11, //Error! UserB 타입에는 age 속성은 없다
isValid: true,
};
const user3: UserA & UserB = { // type1 & type2 -> 병합한 새로운 type이 됨
name: 'B',
age: 11,
isValid: true,
};
//(1) 초기화된 변수의 타입추론
let a = 'Hello';
a = 123; //Error
a = true; //Error
function join(a: string, b: string = ''): string {
return a + b;
}
//(2)기본값을 통해서 b라는 매개변수가 string 타입이라는걸 추론할 수 있음
//(3)들어오는 매개변수가 모두 string 타입이기 때문에 반환타입 또한 추론 가능하다.
function join(a: string, b = ''){
return a + b;
}
join('Hello', 'World');
join('GoodGood');
const btn = document.querySelector('button')
btn.classList.add('btn'); //Error null일수도 있다.
btn.id = 'add' //Error -> 위의 코드처럼 다시 as로 단언해주지 않으면 똑같은 코드를 써줘야만 제대로 동작한다.
const btn = document.querySelector('button') as HTMLButtonElement;
btn.classList.add('btn'); //가능
btn.id = 'add' //가능
//해결방법 단언!
// as 사용 -> btn이라는 변수의 타입을 HTMLButtonElement 타입으로 딱 잘라 타입스크립트에게 전달
//!(Non-null 단언 연산자)
//느낌표가 붙은 앞의 코드가 null이나 undefined가 아니라고 단언해주는다는 개념
// 정확하게 어떤 타입인지는 알 수가 없다.
const btn = document.querySelector('button')!
btn.classList.add('abc');
btn.id = 'abc';
function toTwoDecimals(val: number | string, isNum: boolean){
if(isNum){
(val as number).toFixed(2) //Error -> union 타입이기때문에 val이 string일 경우에는 toFixed를 사용하지 못한다.
}else{
(val as string).slice(0,2) //Error -> union 타입이기때문에 val이 number일 경우에는 slice를 사용하지 못한다.
}
}
function toTwoDecimals(val: number | string, isNum: boolean){
if(isNum){
(val as number).toFixed(2) //가능
}else{
(val as string).slice(0,2) //가능
}
}
toTwoDecimals(3.141414, true)
toTwoDecimals('GOOD', false)
const json = '{"name" : "jth", "age" : 25}'
const user = JSON.parse(json) as {name: string, age: number}
console.log(user.work) // work 속성이 없는데 에러가 나타나지 않음
// 타입스크립트가 문자 데이터 내용까지 이해해서 json으로 바꼈을 때까지 예측할 순 없다.
// 단언 해줘야만 제대로 에러가 나타남
let num: number
console.log(num) //Error => num에는 아무값이 초기화 되있지 않으므로 undefined 값이다. 그렇기 때문에 number 타입이 아니다.
let num!: number //할당 단언
console.log(num) //가능
즉 단언이란
개발자가타입스크립트에게 이 부분은 이런 타입이다 딱 잘라 말하는 개념
타입 단언은 코드상에서 오류가 없더라도 결국 런타임에서 오류가 날 수 있기 때문에타입 가드를 사용하는 것이 좋다!!
const btn = document.querySelector('button');
btn.classList.add('abc'); //Error
btn.id = 'abc'; //Error
const btn = document.querySelector('button');
// null 값이 들어오면 조건 수행이 되지 않기 때문에 훨씬 안전함
if(btn){ //btn이 있을때만 동작!
btn.classList.add('abc'); //Error
btn.id = 'abc'; //Error
}
const btn = document.querySelector('button');
if(btn instanceof HTMLButtonElement){ //HTMLButtonElement의 인스턴스다~
btn.classList.add('abc'); //Error
btn.id = 'abc'; //Error
}
function toTwoDecimals(val: number | string, isNum: boolean) {
if (isNum) {
val.toFixed(2); // Error
} else {
val.slice(0, 2); // Error
}
}
toTwoDecimals(3.14, true);
toTwoDecimals('Good', false);
function toTwoDecimals(val: number | string) {
if (typeof val === 'number') { // 타입이 number 일때만~
val.toFixed(2);
} else {
val.slice(0, 2);
}
}
toTwoDecimals(3.14);
toTwoDecimals('Good');
function isNum(val: unknown) :val is number {
return typeof val === 'number'
}
function toTwoDecimals(val: number | string) {
if (isNum(val)) { // 타입이 number 일때만~
val.toFixed(2);
} else {
val.slice(0, 2);
}
}
toTwoDecimals(3.14);
toTwoDecimals('Good');
user is UserA 처럼 타입 가드가 목적인 함수 반환하는 타입부분에 is라는 키워드를 쓸 수 있다.type UserA = { name: string; age: number };
type UserB = { id: string; email: string };
// 타입 가드 함수 -> UserA 타입인지 확인
function isUserA(user: unknown): user is UserA {
if (user && user.constructor === Object) { // user데이터가 존재하고 && 객체 데이터 일때만
const u = user as UserA; // user는 UserA 타입이라고 단언
//user 매개변수를 UserA 타입처럼 u에 할당
return typeof u.name === 'string' && typeof u.age === 'number';
}
return false;
}
fetch('https://example.com')
.then(res => res.json())
.then((user: UserA | UserB) => {
console.log(user.name[0]) // 문자데이터라면 되야 하지만 오류 발생
console.log(user.age - 10) // 숫자데이터라면 되야 하지만 오류 발생
})
fetch('https://example.com')
.then(res => res.json())
.then((user: UserA | UserB) => {
if(isUserA(user)){ // 타입가드 함수를 사용함으로써 제대로 동작
console.log(user.name[0])
console.log(user.age - 10)
}
})
타입가드 함수를 사용할 때 반환하는 타입에
is키워드를 사용한 반환타입을 제대로 넣어주지 않으면 단순히 다른 함수를 호출하는 개념이므로 그 함수가 실행될 때 까지 타입스크립트는 어떤 타입인지 제대로 알지 못해 오류가 발생한다. 꼭 제대로 넣어줘서 호출 시에 타입가드 함수인지를 알 수 있도록 만들어주자!!!
type MyTypeName = string | number;
type MyArray = MyTypeName[];
type UserA = {
name: string;
age: number;
};
type UserB = {
isValid: boolean;
};
type UserX = UserA & UserB //type Intersection(인터섹션)
const A : MyTypeName = 'a'
const B : MyArray = [1,'A','B',2,3]
const userA : UserA = {
name: 'jth',
age: 25
}
const userB : UserB = {
isValid: false
}
const userX : UserX = {
name: 'jth',
age: 25,
isValid: true
}