ts 타입에 대해 알아보자.
예를 먼저 들자.
// 오류 발생 코드
const player:{
name:string,
age:number
}={
name:'coco'
};
일 때, ts에서는 오류를 내뱉는다. 이유는, name
과 age
를 받는다고 했는데, 실제로는 name
하나만 있기 때문이다.
age
가 꼭 필수가 아닌 경우에는 어떻게 하냐면
//...
age?:number
라고 수정하면 ts오류가 뜨지 않는다.
이것이 optional parmeter(선택적 변수)
를 지정하는 방법이다.
// 수정된 코드
const player:{
name:string,
age?:number
}={
name:'coco'
};
이제 if문으로 age가 10이상일 때 실행하게 될 코드를 적어보자.
// 오류 발생 코드
if(player.age < 10){}
위 코드에서 ts오류가 나타난다. 왜냐하면 age
가 필수 입력값이 아니기때문에 age
의 타입이 number
또는 undefined
가 들어올 수 있기 때문이다.
그래서 player.age
가 있고, 있다면 실행하게끔 수정을 해야한다. &&
연산자를 사용한다.
// 수정된 코드
if(player.age && player.age < 10){}
자, 그렇다면 무수히 많은 player가 있다고 가정해보자.
그러면 코드를 이렇게 작성하게 될 것이다.
const playerJin:{
name:string,
age?:number
}={
name:'jinjin'
};
const playerSol:{
name:string,
age?:number
}={
name:'solsol',
age: 2
};
.
.
.
위의 코드를 보면 어떠한가? 중복된 타입 지정이 거슬리지 않는가??
그렇다. 위의 코드는 중복된 코드를 Alias(별칭)타입
을 생성해서 따로 뺄 수 있다.
코드를 수정해보자
type Player = {
name:string,
age?:number
}
const playerJin:Player={
name:'jinjin'
};
const playerSol:Player={
name:'solsol',
age: 2
};
코드가 한 결 간결하지고 깔끔해졌다.
타입별칭을 사용할 때는 항상 type
으로 선언을 하고 선언명의 첫문자는 꼭 대문자여야 한다.
여기서 더 나아가면, Player라는 별칭을 타입으로 사용하여jin
과 sol
모두 player라는걸 알 수 있기때문에 playerJin
을 jin
으로, playerSol
을 sol
로 변수이름을 수정할 수 있다.
type Player = {
name:string,
age?:number
}
const jin:Player={
name:'jinjin'
};
const sol:Player={
name:'solsol',
age: 2
};
여기서 Alias는 object만 가능한것이 아닌 여러 타입이 가능하다.
type Age = number;
type Name = string;
type Player = {
name: Name,
age: Age
}
하지만 위 처럼 과도하게 재사용하는 것은 좋지 않은 방법이다.
코드가 깔끔하고 명확해질 때까지만 이 작업을 하면 된다.
이번엔 함수의 return값의 타입을 지정하는 방법을 배워보자.
조건: 이 함수는 player의 object를 만들고 그 결과로 player를 반환한다.
type Age = number;
type Name = string;
type Player = {
name: Name,
age: Age
}
function playerMaker(name:string){
return{
name // name:name 과 동일하다. key value가 똑같은 이름이면 하나만 써도 됨.
}
}
위의 코드에서 ts는 우리가 object를 return하는 것만 알고있다. 타입이string인 name이 들어있는 object를 말이다.
하지만 우리는 ts에게 playerMaker는 Player타입을 return하고 있다고 알려주고싶다.
const coco = playerMaker('kko'); // 인자에 아무것도 들어있지 않으면(===playerMaker()) 오류를 발생시킴.
coco.age = 2;
위의 코드에서 playerMaker에는 name만 있는 object를 반환하기 때문에 coco.age
를 찾을 수 없어서 오류를 일으킨다.
하지만 우리는 위에서 말했듯이 Player타입을 return하고 있다고 알려주고싶다.
방법은 간단하다. 함수 뒤에 타입을 명시하면 된다.
type Age = number;
type Name = string;
type Player = {
name: Name,
age: Age
}
function playerMaker(name:string):Player{
return{
name // name:name 과 동일하다. key value가 똑같은 이름이면 하나만 써도 됨.
}
}
const coco = playerMaker('kko');
coco.age = 2;
playerMaker함수에서 인자를 받는 괄호 옆에 콜론:을 쓰고 리턴타입을 명시해주면 된다.
그러면 더이상 coco.age
에서 오류가 생기지 않는다.
ES6문법인 화살표함수로 바꾸어보자
type Age = number;
type Name = string;
type Player = {
name: Name,
age: Age
}
const playerMaker = (name:string) : Player => ({name});
const coco = playerMaker('kko');
coco.age = 2;
js에서는 없는 readonly
동작이 ts에서는 가능하다.
위의 코드를 예로 들어보자
type Age = number;
type Name = string;
type Player = {
readonly name: Name,
age: Age
}
const playerMaker = (name:string) : Player => ({name});
const coco = playerMaker('kko');
coco.age = 2;
coco.name = 'koko'
Player 타입에서 name속성을 readonly로 바꾸었다. 'readonly'
를 앞에 추가하면 읽기전용이 되어 수정이 불가하다.
그러므로 coco.name = 'koko'
로 수정할 때 ts는 오류를 발생시켜준다.
readonly를 다른곳에서도 활용할 수 있다.
const numbersArr : readonly number[] = [1,2,3,4];
numbersArr.push(5);
위 코드처럼 numberArr
변수의 타입을 readonly로 정하면 .push할 때 ts에서 'push라는 것이 readonly number[]타입에 존재하지 않기' 때문에 오류를 발생시킨다.
readonly를 지우면 .push가 잘 동작한다.
배열을 바꾸지 않는 .map이나 .filter는 readonly가 붙어도 사용할 수 있다.
이번엔 Tuple
에 대해 배워보자. tuple도 ts에만 있다.
Tuple
은 array를 생성할 수 있게 한다. 최소한의 길이를 가져야하고, 특정 위치에 특정 타입이 있어야한다.
예를 들어보자
const player : [string, number, boolean] = ['coco',2,true];
player변수는 ts에게 이 array가 최소 3개의 아이템을 가지며, string,number,boolean순서대로여야 한다는 것을 알릴 수 있다.
Tuple
은 항상 정해진 개수의 요소를 가져야하는 array를 지정할 수 있다는 것이 중요하다.
튜플로 타입 선언된 변수를 수정할 때 잘못되었다면 바로 알 수 있다.
const player : [string, number, boolean] = ['coco',2,true];
player[0] = 1; // 오류 발생. 0번째 배열은 항상 string인데 number로 바꿀 수 없다.
readonly와 함께 쓰면 올바른 수정이라 할지라도 수정이 불가능하게 할 수 있다.
const player : readonly [string, number, boolean] = ['coco',2,true];
player[0] = 'koko';
any
타입은 타입을 지정하지 않는다면 기본적으로 타입추론에 의해 any
가 들어가게 된다.
const aa = []; // 여기서 aa의 타입은 any[]로 되어있다.
any
는 쉽게말해서 ts에서 벗어나 js가 되고싶을때나 ts에게 바보같은 짓을 하고싶다고 허락받고 싶을 때 사용한다.
any
는 ts의 모든 보호장치를 비활성화 시킨다.
무분별한 any
는 ts를 활용하지 못하고 있는 것이다. 지양해야한다.
unknown
어떤 타입인지 모르는 변수는 ts에게 어떻게 말해줘야할까? 이럴 때 unknown
을 사용하면 된다.
let a: unknown;
a변수의 타입을 unknown
으로 해두면, 어떤 작업을 할려면 a변수의 타입을 먼저 확인하는 방식으로 할 수 있다.
예를 들어서
let a: unknown;
let b = a + 1;
변수 b를 선언하여 a변수에 1을 더한다고 하면 ts는 오류를 발생시킨다.
왜냐하면 a는 unknowm이기 때문에 number인 1을 더할 수 없기 때문이다
이럴때는 위에서 말한것처럼 a변수의 타입을 먼저 확인하는 방식으로 해야한다.
let a: unknown;
if(typeof a === 'number'){
let b = a + 1;
};
이렇게 typeof
를 사용하여 a변수의 타입이 number인지 먼저 확인한 후에 맞다면 조건문 안의 코드를 수행하게 된다.
또는
let a: unknown;
if(typeof a === 'number'){
let b = a + 1;
};
a.toUpperCase();
변수 a를 upperCase(===대문자) 하고싶지만 a는 unknown이기 때문에 못한다.
이럴때에도 a변수 타입을 먼저 확인하는 방식으로 해야한다.
let a: unknown;
if(typeof a === 'number'){
let b = a + 1;
};
if(typeof a === 'string'){
a.toUpperCase();
};
이러면 에러없이 잘 동작한다.
void
void
는 아무것도 return하지 않는 함수를 대상으로 사용한다.
// void발생 함수
function hello(){
console.log('hi');
}
일 때, 함수 hello타입은 void로 되어 있는걸 확인할 수 있다.
void는 따로 지정할 필요가 없다. ts는 함수가 아무것도 return하지 않는다는것을 자동으로 인식한다.
never
never
타입은 많이 사용하진 않지만 무엇인지 아는 것은 중요하다.
never
는 함수가 절대 return하지 않을 때 발생한다.
function hello():never{
return 'x';
}
일 때 return문에 에러가 발생한다.
하지만 return문에 오류를 발생시키면 오류가 나지 않는다.
// 여기서의 hello함수는 return하지 않고 오류를 발생시키는 함수임.
function hello():never{
throw new Error('x');
}
또한 nenve
는 타입이 두가지 일수도 있는 상황에 발생할 수 있다.
function hello(name:string|number){
if(typeof name === 'string'){
name // 여기서의 name 타입은 string이다.
} else if(typeof name === 'number){
name // 여기서의 name 타입은 number이다.
} else {
name // 여기서의 name 타입은 never이다.
}
}