function plusOne(x :number | string){
return x + 1 //에러남
}
위와 같은 함수가 있다고 보자
x 파라미터는 string | number 와 같은 union 타입인데
숫자 1 을 더하기에는 x 가 string 인지 number 인지 애매하기 때문에 에러를 내게 된다.
따라서 이럴 땐 타입을 하나로 narrowing 해주거나 assert 해주거나 하면 된다.
narrowing 에 대해서 먼저 알아보자
typeof 를 이용한 Type Narrowing
타입 narrowing 이란 if 문으로 타입을 하나로 정하는 것을 말한다.
function plusOne(x :number | string){
if (typeof x === 'number') {
return x + 1
}
else if (typeof x === 'string') {
return x + 1
}
else {
return 0
}
}
위처럼 typeof 라는 키워드를 사용해서
파라미터 x 가 number 라면~
파라미터 x 가 string 이라면~
이런식으로 경우를 나누는 것이다.
(이런식으로 if 문을 쓸 때에 else 까지 닫아주는 것을 권장한다. return 하지 않는 조건문이 있으면 나중에 버그가 생길 수도 있기 때문)
꼭 typeof 를 써야하는 건 아니다.
in 이나 instanceof 라는 키워드도 사용 가능하다.
Type Assertion
타입을 간편하게 assert 할 수도 있다.
function plusOne(x :number | string){
return (x as number) + 1
}
console.log( plusOne(123) )
변수명 as string
이런식으로 작성하면, 이 변수를 string 으로 생각해달라- 라는 의미이다.
실제로 타입을 string 으로 변경해준다.
union type 같이 애매한 타입(복잡한 타입) 을 정확하게 하나의 타입으로 줄이는 역할을 수행한다.
number 인 타입을 string 으로 바꾸려고 할 때 쓰이는 그런 것은 절대 아니다.
위 예시처럼 number 일수도, string 일 수도 있는 데이터 타입을 number 로 확실하게 해주고 싶을 때 쓴다고 보면된다.
as 를 쓰면 굉장히 간편하지만 정확한 코드를 작성하기 위해선 narrowing 을 권장한다.
as 키워드는 엄격한 타입체크기능을 잠깐 안쓰기 위함이다.
as 키워드를 사용하길 권장하는 때는...
- 왜 타입에러 나는지 모르겠어서 임시로 에러해결할때
- 어떤 타입이 들어올지 확실하게 아는데 컴파일러 에러가 날때
정도가 있겠다.
따라서 as 보다는 type narrowing 위주로 사용해보자.
실습해보자
1. 문자 또는 숫자가 들어있는 리스트를 넣으면
모두 숫자로 변환된 요소가 들어있는 리스트가 반환되는 cleaning 이라는 함수를 만들어보자
function cleaning(someList: (number|string)[]){
let result :number[] = []
someList.forEach((x)=>{
if( typeof x === 'string'){
result.push(Number(x))
}
else{
result.push(x)
}
})
return result;
}
console.log(cleaning(['2',7,'2']))
내가 입력받는 리스트에는 number 타입이나 string 타입만 들어갈 수 있는 리스트이므로
someList : (number|string)[]
라고 적어주었다
result 는 number 만 들어갈 수 있는 리스트 이고
someList 안에 있는 요소들을 돌아가면서 typeof 키워드를 통해 데이터 타입을 확인후
데이터 타입이 만약 string 이라면 Number 를 통해 number 로 바꾸어 주었다.
데이터 타입이 number 면 바꿔줄 필요 없으니까 그냥 result 에 냅다 넣었다.
2. 아래같이 여러 변수에 선생님이 가르치는 과목이 저장되어있다.
어떤 선생님의 object 자료를 집어넣으면 가르치고 있는 과목중에 맨 뒤에 1개를 return 해주는 함수를 만들어보자
let 철수쌤 = { subject : 'math' }
let 영희쌤 = { subject : ['science', 'english'] }
let 민수쌤 = { subject : ['science', 'art', 'korean'] }
나는 아래처럼 작성했다.
function lastSubject(teacher: {subject: string|string[]}){
if (typeof teacher.subject === 'string'){
return teacher.subject
}
else if(Array.isArray(teacher.subject)){
return teacher.subject[teacher.subject.length - 1]
}
else{
return 'wrong input';
}
}
console.log(lastSubject({ subject : ['science', 'english'] }));
teacher.subject 가 list인지 확인하는 방법으로
Array.isArray( 리스트 )
를 사용했다.
(+추가) Narrowing 할 수 있는 추가적인 방법을 알아보자
null & undefined 체크하기
프로젝트 개발을 하다보면 데이터를 가져오고 뿌리는 과정에서
은근 null 타입이나 undefined 타입인지 검사하는 경우가 많다
물론 if 문을 써서 '이 변수가 undefined 일 경우 ~~' 처럼 쓸수도 있지만 다른 문법도 배워보자
&& 연산자를 사용한 방법이다.
&& 연산자는 논리연산자로 쓰는데
여러개를 사용하면 의미가 아래와 같다.
&&로 비교할 때 true, false 를 안쓰고 다른 타입의 자료형들을 넣으면
처음 등장하는 falsy 한 값을 찾고, 아니면 마지막 값을 남긴다.
falsy 값은 null, undefinedm NaN 등이 있다.
1 && null && 3 // null이 남음
undefined && '안녕' && 100 // undefined 남음
이 문법을 적용한 예시를 아래서 확인하자
function printAll(strs: string | undefined) {
if (strs && typeof strs === "string") {
console.log(strs);
}
}
if 문을 보자
strs 의 값이 falsy 한 값이 아니면서, 타입이 string 에 해당한다면 if 문 안의 코드가 실행된다.
하지만 falsy 한 값인 경우엔 실행되지 않는다.
이 방법이 꼭 좋다고 할 수는 없다. 오히려 if else 문을 쓰는게 코드가 잘 읽힐 수 있다.
if(변수 != null)
이렇게 써도 null 과 undefined 를 동시에 거를 수 있다.
in 연산자 사용해서 object 자료 narrowing 하기
파라미터로 받은 object 를 narrowing 할 수 있는데,
이 object 가 어떤 object 인지에 따라 경우를 나눌 수 있다.
type Fish = { swim: string };
type Bird = { fly: string };
function 함수(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim
}
return animal.fly
}
예를 들어서 Fish 와 Bird 처럼 unique 한 속성을 가지고 있다고 해보자(swim 과 fly는 각 object 의 고유한 속성일 경우이다)
그러면 animal 이 Fish 랑 Bird 타입일수는 있지만 그중에서도 swim 이라는 속성이 들어있는 경우 - 를 narrwoing 해줄 수 있다.
class 로부터 만들어진 object 라면 instanceof로 narrowing 하기
어떤 object 들은 어떤 부모 클래스로부터 만들어진 경우도 있는데
이럴 땐 instanceof 키워드를 사용해서 부모 클래스가 누군지 검사할 수 있다.
예를 들어서 new 키워드로 object 를 만들 수 있는 것들 중 하나가 날짜이다.
(ex: new Date())
let 날짜 = new Date();
if (날짜 instanceof Date){
console.log('참이에요')
}
위처럼 '날짜' 가 Date 로 부터 만들어진 object 인지 구분하는 방법이다.
literal type 과 narrowing
type Car = {
wheel : '4개',
color : string
}
type Bike = {
wheel : '2개',
color : string
}
function 함수(x : Car | Bike){
if (x가 Car타입이면요){
console.log('이 차는 ' + x.color)
} else {
console.log('이 바이크는 ' + x.color)
}
}
Car 그리고 Bike 라는 타입이 있다
위와 같은 경우에는 x 가 Car 타입인지에 대해 narrowing을 어떻게 해주어야할까?
Car과 Bike 를 구분하기에는 wheel 과 color 둘 다 가지고 있다(이런일은 별로 없겠지만.. 있다고 가정해보자)
type Car = {
wheel : '4개',
color : string
}
type Bike = {
wheel : '2개',
color : string
}
function 함수(x : Car | Bike){
if (x.wheel === '4개'){
console.log('the car is ' + x.color)
} else {
console.log('the bike is ' + x.color)
}
}
object 들을 구분할 일이 많다면 literal type을 만들어두면 편하다
그러면 비슷한 object 가 들어와도 literal type 으로 narrowing 이 가능하다
예를 들어 위처럼 wheel 속성에 있는 값이 4인가 물어봐서 맞다면 무조건 Car 타입인 것이다.
ref: 코딩애플