본문 바로가기

IT

Effective Typescript - 41 - any의 진화를 이해하기

타입스크립트의 타입은 변수를 선언할 때 결정된다. 하지만 any 타입과 관련해서 예외인 상황이 있습니다.

아래 예제가 바로 그러한 상황입니다.

function range(start: number, limit: number) {
    const out = []; // any[]
    for (let i = start; i < limit; i++ ){
        out.push(i);
    }
    return out; // 반환 타입이 number[]로 추론됨.
}

out의 타입은 최초 변수가 선언되었을 때, any[]로 추론되었지만 number 타입을 push 한 순간부터 타입은 number[]로 진화(evolve)합니다.

타입의 진화는 타입 좁히기와는 다릅니다. 배열에 다양한 타입 요소를 넣으면 배열의 타입이 확장되면서 진화합니다.

const result = []; // any[]
result.push('a'); // string[]
result.push(1); // (string | number) []

또한 조건문에서는 분기에 따라 타입이 진화합니다.

let val; // any
if(Math.random() < 0.5){
    val = /hello/
    val; //타입이 RegEx
} else{
    val = 12;
    val;//타입이 number
}
console.log(val) // 타입이 RegEx | number

let val2 = null;
try{
    someDangerousWork();
    val2 = 12; // number
    val2 // number
}catch(e){
    console.warn(e);
    val2 // number | null
}
val2; // number | null

하지만 변수 선언 시 타입을 any로 명시하면 any의 진화는 더 이상 일어나지 않습니다.

let val: any; // any
val = 12 // any

암시적 any 상태인 변수에 어떠한 할당도 하지 않고 사용하려고 하면 암시적 any 오류가 발생합니다.

function range(start: number, limit: number) {
    const out = [];
    // 오류 Variable 'out' implicitly has type 'any[]' in some locations where its type cannot be determined.(7034)
   
    if(start === limit){
        return out; // 오류 Variable 'out' implicitly has an 'any[]' type.(7005)
    }

    for (let i = start; i < limit; i++){
        out.push(i);
    }
    return out;
}

any 타입의 진화는 암시적 any 타입에 어떤 값을 할당할 때만 발생합니다. 그리고 어떤 변수가 암시적 any 상태일 때 값을 읽으려고 하면 오류가 발생합니다.

 

암시적 any 타입은 함수 호출을 거쳐도 진화하지 않습니다. 아래 예제를 통해 확인할 수 있습니다.

function range(start: number, limit: number) {
    const out = [];
    // 오류 Variable 'out' implicitly has type 'any[]' in some locations where its type cannot be determined.(7034)
   
    if(start === limit){
        return out; // 오류 Variable 'out' implicitly has an 'any[]' type.(7005)
    }

    for (let i = start; i < limit; i++){
        out.push(i);
    }
    return out;
}


function makeSquares(start: number, limit: number){
    const out = [];
    //Variable 'out' implicitly has type 'any[]' in some locations where its type cannot be determined.

    range(start, limit).forEach(i =>{
        out.push(i * i);
    });
    return out;
    //Variable 'out' implicitly has an 'any[]' type.(7005)
}

 

* 일반적인 타입들은 정제되기만 하는 반면, 암시적 any와 any[] 타입은 진화할 수 있습니다. 이러한 동작이 발생하는 코드를 인지하고 이해할 수 있어야 합니다.

* any를 진화시키는 방식보다 명시적 타입 구문을 사용하는 것이 안전한 타입을 유지하는 방법입니다.