타입스크립트 자체적으로 값의 타입을 결정하는 과정.
명시적으로 개발자가 타입을 지정해주지 않아도 값의 타입을 예측 또는 결정해준다.
개발자가 일일히 모든 타입을 명시적으로 선언해주어야만 한다면
코드의 가독성 측면에서나 개발 생산성 측면에서 좋지 않다.
또한, 개발자가 놓칠 수 있는 타입 오류를 미리 잡아내는 역할도 할 수 있다.
간단하게 예를 들자면,
const number = 123;
이러한 변수에 대해서 명시적으로 타입을 지정해주지 않았지만,
타입스크립트의 컴파일러는 초기값 123을 바탕으로 number의 타입을 'number'로 추론한다.
아주 똑똑한 친구가 아닐 수 없다.
let num = 123 // num : number
const greet = "hello" // greet : string
let nullData = null // nullData : any
let undefinedData; // undefinedData : any
let foo; // foo: any
foo = 'Hello';
console.log(typeof foo); // string
foo = true;
console.log(typeof foo); // boolean
function log(value){ // value 매개변수에는 암시적으로 any 형식이 포함됩니다.
console.log(value)
}
function add(a, b) { // a와 b는 any 타입으로 추론.
return a + b; // 반환 값은 number 타입으로 추론.
}
export default function Modal({ children }: Props) {
function getValue(n: number) {
switch (n) {
case 1:
return "hello";
case 2:
return 100;
case 3:
return true;
default:
return { n };
}
}
//(local function) getValue(n: number): true | "hello" | 100 | {n: number;}
let user = {
name: "John",
age: 25
}; // user : { name: string; age: number; }
특정 스코프 내에서 변수의 구체적인 타입을 추론할 수 있다.
보통은 typeof, instanceof 를 이용한 타입가드를 사용한다.
function func(x:number|string){
if(typeof x === 'string'){
console.log(x.toUpperCase) // string method
}else {
console.log(x.toFixed) // number method
}
}
유니언 타입(|)은 여러 타입 중 하나일 수 있는 값을 나타낸다.
유니언 타입의 값에 접근할 때 해당 타입들의 공통 속성만 접근할 수 있다.
인터섹션 타입(&)은 여러 타입을 하나로 결합한다.
인터섹션 타입의 값은 그에 포함된 모든 타입의 속성을 포함해야 한다.
type Dog ={name:'dog', bark:"멍멍"}
type Cat ={name:'cat', meow:"냥냥"}
function animal(animal:Dog|Cat){
if('bark' in animal){
console.log('animal is dog')
} else if('meow' in animal){
console.log('animal is cat')
}
}
표현식의 타입이 해당 컨텍스트에 의해 결정되는 기능.
타입스크립트가 자동으로 컨텍스트를 검토하여 타입을 추론한다.
정말 똑똑하다!
특히, 이벤트 핸들러와 같은 곳에서 유용하다.
window.onmousedown = function(e) {
console.log(e.button)
}
위의 예시에서 매개변수 e는 window.onmousedown 이벤트 핸들러에 의해
MouseEvent 타입을 자동으로 추론한다.
명시적으로 타이핑을 하지 않아도 되기 때문에 코드가 간결해진다.
타입을 단언하는 것은 타입스크립트에게
'너의 추론은 잘 알겠는데, 내가 맞아' 라고 알려주는 방법이다.
as 키워드를 사용할 수 있다.
JS 코드를 TS로 변환할 때 가끔 사용한다고 한다.
let foo = {}
foo.bar = 123; // {} 형식에 bar 속성이 없습니다.
foo.bas = 'hello' // {} 형식에 bas 속성이 없습니다.
interface Foo {
bar:number;
bas:string;
}
let foo2 = {} as Foo;
foo2.bar = 123;
foo2.bas = 'hello'
타입을 단언하는 것은 타입추론을 부정하는 일이므로,
타입의 안정성을 해칠 수 있으므로 신중하게 사용해야 한다.
공식문서에는 '해롭다' 고 표현한다.
유니온 타입을 선택하거나 배열의 요소 타입을 결정하는데 사용되는 추론 로직이다.
타입 추론에 의해 배열 리터럴의 타입을 결정하기 위해 각 요소의 타입을 고려한다.
예를 들어, 아래 예시 배열의 타입은 number[].
let arr = [0, 1, null];
하지만 배열의 모든 요소가 공통된 단일 타입을 공유하지 않는 경우,
각 요소의 타입에서 최적화된 공통 타입을 추론한다.
let arr = [0, "1"]; // arr : (number | string)[]
배열의 요소가 number 혹은 string 을 담고 있기 때문에,
타입스크립트는 위 배열의 타입을 "number 와 string의 유니온 타입 배열"
로 추론한다.
이러한 매커니즘을 통해 '모든 타입이 공유할 수 있는 가장 정확한 타입'을 찾는데,
이를 최적화된 공통 타입(Best Common Type)이라 한다.
타입스크립트는 아주 똑똑하고 귀여운 친구이지만,
항상 정확하게 타입을 추론한다고 볼 수는 없다.
예를들어 함수의 리턴값을 추론하는 경우, 타입스크립트는
함수의 리턴값의 모든 경우의 수를 포함하는 가장 포괄적인 타입을 추론한다.
이것은 개발자의 의도와 다를 수 있기 때문에 함수의 리턴에는
명시적으로 타입을 지정해 주는 것이 조금 더 바람직하다.
또한, 제네릭을 사용할 때도 함수 호출에서 사용되는 인수의 타입을 사용하게 되는데,
명확하게 제네릭 타입을 지정하지 않으면 any 타입을 추론할 수 있다.
따라서 제네릭을 사용할 경우에는 명시적으로 타입을 지정해주는 것이 좋다.