타입스크립트는 strongly-typed 언어이다.
strongly-typed 이란, 프로그래밍 언어가 작동하기 전에 type을 확인한다는 것이다.
❗️자바스크립트는 strongly-type이 아니다.
typeof
연산자가 있다.타입 안전성
1. 코드에 버그가 줄게된다.
2. 런타임 에러가 줄여진다.
→ 런타임에서 오류가 발생하기전에 막아준다. 즉, 개발 도중에 오류를 확인할 수 있다.
3. 생산성 증가
[1, 2, 3, 4] + false
//결과 : '1,2,3,4false'
function divide(a, b){
return a / b
}
divide(2, 3) //0.66666666666
divide("xxxxx") //NaN
divide("xxxxx")
divide는 입력값이 두 개인데 하나밖에 보내지 않았는데 실행된다.결론 : 자바스크립트는 a와 b가 필수 입력값인지 선택사항인지 전혀 모른다.
런타임 에러는 콘솔 안에서 일어나는 에러이다.
런타임 에러는 유저의 컴퓨터에서 코드가 실행될 때만 발생한다.
자바스크립트는 유저가 코드를 실행했을 때야 비로소 에러가 있다는 걸 알 수 있다. 하지만, 타입스크립트에서 이러한 에러는 코드를 실행하기 전에 최소화시킬 수 있다.
타입스크립트 코드를 컴파일하면, 보호장치 없는 일반적인 자바스크립트 코드가 된다. 그리고 타입스크립트 코드에 에러가 있으면 그 코드는 자바스크립트로 컴파일되지 않는다. 이러한 기능이 유저가 코드를 실행하는 런타임에 발생하는게 아니고 개발중에 발생하는 에러이기 때문에 런타임에러를 피할 수 있다.
TypeSciprt는 두가지 접근방식을 결합했다.
let a = "hello"
a= "bye"
a =1 //ts -> error & js -> error없음
❗️자바스크립트에서는 그냥 변수를 생성만 하면서 그 변수가 어떤 타입인지는 지정해주지 않아도 된다.
let b : boolean = false
❗️typeScript의 type checker에게 타입을 추론하는 것을 허용하고 싶다면 사용X
let c : number[] = []
c.push("1") //error
배열에 아무것도 들어있지 않을 때 타입스크립트가 추론을 못하기 때문에 명시적으로 표현해줘야된다.
데이터와 변수의 타입을 명시적으로 정의할 수 있다. 하지만 그냥 JavaScript처럼 변수만 생성하고 넘어가도 된다. 왜냐하면 TypeScript가 타입을 추론해 주기 때문이다.
결론 : 명시적 표현은 최소한으로 사용하는게 좋다.
즉, 타입스크립트가 추론하는게 더 좋다.
특정 타입이나 인터페이스를 참조할 수 있는 타입 변수를 의미한다.
타입별칭을 사용하여 객체 타입뿐만 아니라 모든 타입에 이름을 지정할 수 있다.
ex)
//string 타입을 사용할 때
const name : string = 'capt';
//타입 별칭을 사용할 때
type MyName = string;
const name : MyName = 'capt';
인터페이스의 모든 프로퍼티가 필요한 것은 아니다. 어떤 조건에서만 존재하거나 아예 존재하지 않을 수 있다. 그렇기 때문에 선택적 프로퍼티들을 사용하여 객체 안의 몇 개의 프로퍼티만 채워 함수를 전달하는 option bags
같은 패턴을 만들 때 유용하다.
선택적 프로퍼티를 가지는 인터페이스는 다른 인터페이스와 비슷하게 작성되고, 선택적 프로퍼티는 선언에서 프로퍼티 이름 끝에 ?
를 붙여 표시한다.
ex)
선택적 프로퍼티를 사용하지 않을 경우 → ❗️에러 발생
ex)
선택적 프로퍼티를 사용할 경우 → 에러 없음
?
추가
type Player = {
name : string,
age ?: number
}
const nico : Player = {
name : "nico"
}
const lynn : Player = {
name : "lynn",
age : 12
}
예제)
//type Aliases 사용
type Name = string;
type Age = number;
type Player ={
name : Name,
age ?: Age
}
function playerMaker(name : string){
return {
name
}
}
const nico = playerMaker("nico")
nico.age = 12 //error
nico 객체에 age를 추가하면 에러가 발생한다.
이유 : playerMaker는 string인 name이라는 요소만 있는 object를 return하고 있기 때문이다. 따로 return 값을 설정해주어야 한다.
해결방법
TypeScript에게 playerMaker 함수는 Player 타입을 return하고 있다고 말해주어야 한다.
일반함수 return 타입 지정
//type Aliases 사용
type Name = string;
type Age = number;
type Player ={
name : Name,
age ?: Age
}
function playerMaker(name : string) : Player{
return {
name
}
}
const nico = playerMaker("nico")
nico.age = 12
type Name = string;
type Age = number;
type Player ={
name : Name,
age ?: Age
}
const playerMaker = (name : string) : Player => ({ name })
const nico = playerMaker("nico")
nico.age = 12
typescript에서
let resultPhrase = 'Result is: ';
resultPhrase = 0; // 이 부분에서 에러가 발생한다.
타입추론에 의해서 resultPhrase는 스트링이 올꺼라고 예상했다.
const player : [string, number, boolean] = ["nico",1,true]
player[0] = "hi"
const q: any[] = [1, 2, 3, 4];
const z: any = true;
q + z; //에러발생 안됨.
'이거 혹은 저거'와 같은 타입을 유니언타입이라고 한다.
두가지 타입을 설정할 때 사용한다.
let name : string | number = "kim";
let age : (string | number) = 100;
let arr : (number | string)[] = [1, '2', 3];
let obj : { data : (string | number) } = { data : '123' };
매개변수를 유연하게 받을 수 있다.
어떤 타입을 받느냐에 따라 함수에 다른 로직이 필요할 수 있다. 이럴 때 유니언 타입을 사용한다.
// 유니언타입 사용
function combine(input1: number | string, input2: number | string) {
let result;
if (typeof input1 === 'number' && typeof input2 === 'number') {
result = input1 + input2;
} else {
result = input1.toString() + input2.toString();
}
return result;
}
const combinedAges = combine(30, 26);
console.log(combinedAges);
const combinedNames = combine('Max', 'Anna');
console.log(combinedNames);
유니온 타입은 타입2개를 합쳐서 새로운 타입을 만들어냄
ex1)
왜 타입이 맞는데 +1이 안되는것?
타입스크립트는 타입 엄격한거 좋아한다.
리터럴 타입은 특정 변수나 매개변수가 어떤 타입이어야 하는지 정의하는게 아니다.
숫자나 문자열이라고 하는게 아니라 정확히 어떤 값이어야 하는지 정의하는 것이다.
단점은 개발자로서 리터럴타입을 기억해야한다.
리터럴 타입을 유니언 타입으로 묶는 것이다.
resultConversion : 'as-number' | 'as-text'
리터럴 타입은 string이나 number와 같은 핵심 타입을 바탕으로 하지만 특정 버전의 타입을 사용할 수 있다. 예) 특정 문자열
resultConversion : 'as-number' | 'as-text'
유니언 타입을 사용할 때 이렇게 계속 반복해서 적는 것은 번거롭다.
그렇기 때문에 type alias
를 사용하면 된다.
undefined
자바스크립트에서 실제로 어떤 값이다.
즉, 존재하지 않는 속성이나 객체에 접근할 때 나타나는 값이다.
기술적으로
void가 undefined를 반환하고 있다는 것이다.
헷갈리는 점
undefined 자체가 타입스크립의 타입이라는 것이다.
undefined를 타입으로 사용할 수 있다.
function printResult(num: number) : undefined{
console.log('Result ' + num);
}
printResult(add(5,12));
단, 함수가 아무것도 반환하지 않을때 void는 쓸 수 있지만 undefined는 못쓴다.
void는 명시적으로 이 함수에 반환 구문이 없다고 알려주는 것이다.
function printResult(num: number) : undefined{
console.log('Result ' + num);
return
}
printResult(add(5,12));
위 코드는 undefined라고 하면 타입스크립트는 반환 구문이 있지만 아무것도 반환하지 않는다고 이해한다. 오류가 발생하지 않는다.
자바스크립트 관점에서는 아래 두 코드 본질적으로 동일하다.
function printResult(num: number) : undefined{
console.log('Result ' + num);
}
printResult(add(5,12));
function printResult(num: number) : undefined{
console.log('Result ' + num);
return
}
printResult(add(5,12));
하지만 타입스크립트에서는 다르다. 그래서 반환 구문이 없을 때는 void를 쓰고 거의 쓸일이 없지만 반환 구문은 있지만 실제로 아무 값도 반환하지 않을 때는 undefined를 쓰면 된다. 이건 드믄 경우이다. 그냥 void를 사용해도 된다.
타입 체크 해제 기능
콜백 함수를 정의 시 장점
함수 안에서 콜백을 전달하면 타입스크립트는 결과가 숫자라는 것을 추론한다.
단, 타입스크립트가 잡지 않는 오류가 있다. 반환이다....
이것은 타입스크립트에서 버그나 실수가 아니다.
콜백 타입을 void라고 명시함으로써 여기에서 반환되는 결과를 무시하겠다고 의도적으로 밝힌다.
function add(n1: number, n2: number) {
return n1 + n2;
}
function printResult(num: number): void {
console.log('Result: ' + num);
}
function addAndHandle(n1: number, n2: number, cb: (num: number) => void) {
const result = n1 + n2;
cb(result);
}
printResult(add(5, 12));
let combineValues: (a: number, b: number) => number;
combineValues = add;
// combineValues = printResult;
// combineValues = 5;
console.log(combineValues(8, 8));
// let someValue: undefined;
addAndHandle(10, 20, (result) => {
console.log(result);
});
퀴즈
아래 코드는 컴파일을 통과할 수 있는가?
function sendRequest(data: string, cb: (response:any) => void) {
return cb({data: 'Hi there!'});
}
sendRequest('Send this!', (response) => {
console.log(response);
return true;
})
통과할 수 있다.
콜백 함수는 자신이 전달되는 인수가 반환 값을 기대하지 않는 경우에도 값을 반환할 수 있다.
변수의 타입을 미리 알지 못할 때 사용
type of a
가 number인지 확인하는 코드 작성
let a: unknown;
if (typeof a === "number") {
let b = a + 1;
}
if (typeof a === "string") {
let b = a.toUpperCase();
}
never는 함수가 절대로 return 하지 않을 때 발생한다.
❗️많이 사용하지는 않는다.
함수에서 exception(예외) 발생할 때
function hello():never{
//return "X" //never 타입을 사용했기 때문에
throw new Error("X");
}
활용 - else{ name }
은 절대 실행 되지 않는다.
function hello(name: string | number) {
if (typeof name === "string") {
name;
} else if (typeof name === "number") {
name;
} else {
//코드 작동 X
name;
} //여기에 뭘 작성하든 이 타입은 never가 될 거다.
}
const names: readonly string[] = ["1", "2"];
names.push("3"); //readonly 때문에 push불가능.
//error message : Property 'push' does not exist on type 'readonly string[]'.