interface Person {name: string};
const alice: Person = { name: 'Alice' }; // 타입 선언
const bob = { name: 'Bob' } as PErson; // 타입 단언
타입 선언 : 그 값이 선언된 타입임을 명시
타입 단언 : 타입스크립트가 추론한 타입이 있더라도 Person(단언한) 타입으로 간주
타입 선언 -> 할당되는 값이 해당 인터페이스를 만족하는지 검사
타입 단언 -> 강제로 타입을 지정해서 타입 체커가 오류를 무시하도록 만듬
✅ 결론 : 타입 단언 보다는 타입 선언을 사용하자!
➕ 알아가기
원래 타입 단언의 문법은const bob = <Person>{}
이다.
그러나<Person>
이.tsx
파일에서 컴포넌트 태그로 인식되기 때문에 현재는 잘 쓰이지 않는다.
ex1) DOM 엘리먼트 -> 타입스크립트는 DOM에 접근할 수 없기 때문에 #myButton이 버튼 엘리먼트인지 알지 못한다!
const divEl = document.querySelector("#myButton");
if (divEl) {
divEl.addEventListener("click", (e) => {
const button = e.currentTarget as HTMLButtonElement;
button;
});
}
ex2) null이 아님을 단언하는 경우 : 접미사 !
는 그 값이 null이 아니라는 단언문으로 해석됨. <-> 우리가 자주 쓰는 접두사 !
은 boolean의 부정문
const elNull = document.getElementById("foo"); //HTMLElement | null
const ell = document.getElementById("foo")!; //null이 아님을 단언 --> HTMLElement
ex3) 임의의 타입 간의 변환
const el = document.body as Person; // 오류 : Person과 HTMLElement는 서로의 서브타입이어야 가능함.
// 이 오류를 해결할 때 unknown 타입 사용 가능. unknown은 모든 타입의 서브타입이기 때문
const el = document.body as unknown as Person;
❗ 그러나 강제로 임의의 타입 간의 변환을 가능케 하는 것이므로 이런식의 unknown
사용은 권장 ❌
Person[]
이기를 원하는 상황interface Person {
name: string;
}
// Person[]를 원했지만 결과는 {name: string}[]
const people = ["alice", "bob", "jan"].map((name) => ({ name }));
const people = ["alice", "bob", "jan"].map((name) => ({} as Person));
const people_ = ["alice", "bob", "jan"].map((name) => {
const person: Person = { name };
return person;
});
🔽좀 더 간결하게 하자면?🔽
const people: Person[] = ["alice", "bob", "jan"].map((name) => ({ name }));
❓ 그런데 기본형인 string
의 경우 메서드를 가지고 있는 것처럼 보인다!
ex)
'primitive'.charAt(e)
// > "m"
x = "hello" //string
x.language = 'English' //String 객체로 변환
x.language // undefined : 객체 버려짐
function getStringLen(foo: String) {
return foo.length;
}
// 둘다 정상
getStringLen("hello");
getStringLen(new String("hello"));
// 오류 : string 을 매개변수로 받는 메서드에 String 객체를 전달하면 문제가 발생
function isGreeting(phrase: String) {
return ["hello", "good day"].includes(phrase);
}
-> 기본형 타입을 객체 래퍼에 할당하는 구문은 쓰지 않는게 좋다!
➕) 그러나 BigInt랑 Symbol은 기본형을 생성하므로 사용해도 좋음.
✅ 결론 : 타입스크립트 객체 래퍼 타입은 지양하고, 기본형 타입을 사용하자!
interface Room {
numDoors: number;
ceilingHeightFt: number;
}
const r: Room = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: "present", // 오류 : 'Room' 형식에 'elephant'가 없습니다.
};
👉 잉여 속성 체크를 통해서 구조적 타입 시스템에서 발생할 수 있는 중요한 종류의 오류를 잡을 수 있다!
👉 잉여 속성 체크를 통해서 의도와 다르게 작성된 코드까지 찾을 수 있다. ex) darkmode가 아닌 darkMode로 쓰는 경우
❓ 그런데 구조적 타이핑 관점에 의해 오류가 발생하지 않아야 하는거 아니야 ?!
▶ 임시 변수를 도입해보자! obj 객체는 Room 타입에 할당 가능.
const obj = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: 'present',
};
const r: Room = obj; // 정상
obj 타입은 {numDoors: number; ceilingHeightFt: number; elephant: string}
으로 추론되므로 Room 타입의 부분집합을 포함해서 Room에 할당 가능하게 되는 것
interface LineChartOptions {
logscale?: boolean;
invertedYAxis?: boolean;
areaChart?: boolean;
}
const opts = { logScale: true};
const o : LineChartOptions = opts; //오류 : '{ logScale: boolean; }' 유형에 'LineChartOptions' 유형과 공통적인 속성이 없습니다.
✅ 결론 : 임시 변수를 도입했을 때 잉여 속성 체크가 일어나지 않는 한계를 조심하자.
속성을 기본형에 할당하면 그 속성이 없어진다니..!! 타입스크립트에서는 객체 타입 쓰는 걸 조심히 해야겠네요..!! 그리고 임시 변수를 썼을 때, 잉여 속성 체크를 우회할 수 있다는 것이 장점이자 단점이 될 수 있을 것 같군요!!! 좋은 글 잘 읽었습니다 :)