타입추론
타입 추론이 된다면 명시적 타입 구문이 필요하지 않음
비구조화 할당문은 모든 지역변수의 타입이 추론되도록 함
interface Product={
id:string,
name:string,
price:number
}
function logProduct(product:Product){
const {id,name,price}=product;
}
⇒ 따라서 타입 정보가 있는 라이브러리에서 콜백함수의 매개변수 타입은 자동으로 추론됨
잉여 속성 체크
타입에 선언된 속성 외에 속성이 있는지 체크함
객체 리터럴에서만 잉여속성 체크가 동작함 따라서 엄격한 객체 리터럴 체크라고도 한다.
함수의 반환에 타입 명시
⇒ 함수의 반환에 타입을 명시하면, 오류가 정확하게 표기됨
linter의 no-inferrable-types
⇒ 작성된 모든 타입 구문이 정말로 필요한 지 확인 가능함
변수의 값은 바뀔 수 있지만, 그 타입은 보통 바뀌지 않는다.
let id="123456"
fetchProduct(id);
id=123456;
fetchProductBySerialNumber(id);
⇒ 오류가 발생함
고친 코드
let id:string|number="123456";
fetchProduct(id);
id=123456;
fetchProductBySerialNumber(id);
그러나, 이는 혼란을 가중시킬뿐이며, 다른 타입의 변수는 따로 선언하여 사용하자.
좁은 타입의 변수 선언하기
interface Vector{x:number, y:number, z:number}
function getComponent(vector, axis:'x'|'y'|'z'){
return vector[axis];
}
let x= 'x';
getComponent(x);
⇒ 오류가 발생함
x의 추론 타입이 string이기 때문임
이런 경우 const를 사용하여 해결 가능
interface Vector{x:number, y:number, z:number}
function getComponent(vector, axis:'x'|'y'|'z'){
return vector[axis];
}
**const x= 'x';**
getComponent(x);
const는 값이 변하지 않을 것이기 때문에 가장 좁은 값으로 추론됨
따라서 여기서의 x는 type이 ‘x’인 상태로 추론됨
그러나 배열, 객체의 경우에는 여전히 문제
해결방안
- 명시적 타입 구문 제공
const v:{x:1|2|3}={ x:1, };
- 타입체커에 추가적인 문맥 제공
- const 단언문 → as constv1 은 x:number, y:number 형태v3는 readonly x:1 readonly y:1형태로 선언됨
- v2는 x:1 y:number형태
const v1={ x:1, y:2, }; const v2={ x: 1 as const, y:2, } const v3={ x:1, y:1, } as const
타입 좁히기
const el=document.getElementById('foo'); <- HTMLElemnet|Null
if (!el) throw new Error("Unable to find #foo");
el; <-HTMLElement만 남게됨
el.innerHTML='Party'.blink();
실수하는 사례
- null 역시 object이다.
const el=document.getElementById('foo');
if(typeof el==='object'){
el;
}
놀랍게도 typeof null은 “object”이다. 따라서 해당 조건문은 실행되지 않음
- 공백을 통한 조건문
function foo(x?:number|string|null){
if(!x){
x;
}
}
이경우 0, ‘ ‘, null 모든 경우가 false가 되기 때문에 전혀 걸러지지 않았다.
명시적 타입을 통해 걸러내기
interface UploadEvent {type:'upload'; ...}
interface DownloadEvent{type:'download'; ...}
type AppEvent=UploadEvent|DownloadEvent;
function hadnleEvent(e:AppEvent){
switch(e.type){
case 'download':
e
break;
case 'upload':
e
break;
}
}
사용자 정의 타입 가드
function isDefined<T>(x:T|undefined): x is T{
return x!==undefined;
}
객체 생성
⇒ 앵간하면 한번에 만드는 것이 좋다.
나중에 추가해야 하는 경우 as Interface(type)을 사용하여 만들면 된다.
const pt= {} as Point;
pt.x=3;
pt.y=2;
객체전개연산자
const pt= {x:3, y:4};
const id={name:"dong"};
const newObj={...pt,...id};
객체 전개연산자로 선택적 필드 만들기
오류 case
declare let hasDates:boolean;
const nameTitle={name:"blabla", title="blablabla"};
const pharaoh={
...nameTitle,
...(hasDates? {start:-2589, end:-2566}:{})
};
⇒ 이렇게 하면 pharaoh.start를 호출할 수 없다.
호출하려면
function addOptional<T extends object, U extends object>(a:T, b:U|null):T&Partial<U>{
return {...a,...b};
}
const pharaoh=addOptional(
nameTitle,
hasDates?{start:-2589, end:-2566}:null
}
pharaoh.start
별칭 관리
const arr=[1,2];
const alias=arr[0];
arr[0]=1;
console.log(alias);
⇒ 1
별칭을 신중하게, 일관성 있게 사용할 것 → 별칭은 ts가 타입을 좁히는 것을 방해함
한번 별칭을 사용했다면, 이후에는 계속해서 별칭을 사용하는 것이 좋음
문맥상 타입추론
오류나는 코드
type Language='Javascript'|'Typescript'|'Python';
function setLanguage(laguage:Language){
...
}
setLanguage(Javascript) -> 정상
let lang='Javascript';
setLanguage(lang); -> 오류
⇒ let으로 lang을 선언하여 ts가 lang의 타입을 string으로 추론하여 setLanguage에 할당할 수 없게 된다.
해결방법
- 값 제한
let language=Language:'Javascript';
setLanguage(language);
- 상수형 선언
const language='Javascript';
setLanguage(language);
튜플 사용시의 문제점
오류 코드
function panTo(where:[number,number]){ ... }
panTo([10,10]);
const loc= [10,10];
panTo(loc) -> 오류 발생
log는 number[] 타입인데 비해 where은 튜플형태이므로 오류가 발생한다.
해결방법
- 명확하게 튜플로 loc선언
const loc : [number,number] = [20,20]; panTo(loc);
- 상수문맥을 제공하기
이렇게 사용하면, 정의한 곳이 아니라 사용한 곳에서 오류가 발생하므로 주의해야 함function panTo(where:readonly [number,number]){ ... } const loc = [10,20] as const; panTo(loc)
- const는 값이 가리키는 참조가 변하지 않는 얕은 상수, as const는 값이 내부까지 상수라는 사실을 알려줌, 다만 이럴때는 함수에도 readonly 형태로 선언해주어야함