✔ 넓히기
타입스크립트에서의 과정으로 지정된 단일 값을 가지고 할당 가능한 값들의 집합을 유추해야 한다는 뜻
✔ 넓히기의 과정을 제어하는 방법
✔ 타입스크립트의 기본 동작을 재정의(타입 추론의 강도를 직접 제어) 하는 방법
const v: { x: 1 | 3 | 5 } = {
x: 1,
};
const v1 = {
x: 1,
y: 2,
}; // type : {x: number; y: number;}
const v2 = {
x: 1 as const,
y: 2,
}; // type : {x: 1; y: number;}
const v3 = {
x: 1,
y: 2,
} as const; // type : {readonly x: 1; readonly y: 2;}
👉 값 뒤에 as const를 작성하면 타입스크립트느 최대한 좁은 타입으로 추론한다.
✔ 좁히기
타입스크립트가 넓은 타입으로부터 좁은 타입으로 진행하는 과정. ex) null 체크
const el = document.getElementById("foo"); // type : HTMLElement | null
if (el) {
// type : HTMLElement
el.innerHTML = "1";
} else {
// type : null
alert("no element");
}
if (!el) throw new Error("no element"); // type : null
el.innerHTML = "1"; // type : HTMLElement
function contains(text: string, search: string | RegExp) {
if (search instanceof RegExp) {
search; // type : RegExp
return !!search.exec(text);
}
search; // type : string
return text.includes(search);
}
interface A {
a: number;
}
interface B {
b: number;
}
function pickAB(ab: A | B) {
if ("a" in ab) ab; // type : A
else ab; // type : B
ab; // type : A | B
}
function contains(text: string, terms: string | string[]) {
const termList = Array.isArray(terms) ? terms : [terms];
termList; // type : string[]
}
function isInputElement(el: HTMLElement): el is HTMLInputElement {
return "value" in el;
}
function getElementContent(el: HTMLElement) {
if (isInputElement(el)) {
el; // type : HTMLInputElement
return el.value;
}
el; // type : HTMLElement
return el.textContent;
}
interface Point {
x: number;
y: number;
}
const pt: Point = {}; // ❌ ~'{}' 형식에 'Point' 형식의 x, y 속성이 없습니다.
const pt: Point = {
x: 3,
y: 4,
}; // 정상
// 객체를 반드시 제각각 나눠서 만들어야 할땐 단언문을 사용하자
const pt = {} as Point;
pt.x = 3;
pt.y = 4;
✔ 작은 객체들을 조합해서 큰 객체를 만들어야 하는 경우
const pt = { x: 3, y: 4 };
const id = { name: "Pythagoras" };
const namedPoint = {};
Object.assign(namedPoint, pt, id);
namedPoint.name; // ❌ ~~~ '{}' 형식에 'name'속성이 없습니다.
// 객체 전개 연산자 (...) 사용
const namedPoint = { ...pt, ...id };
namedPoint.name; // 정상
✔ 객체에 조건부로 속성을 추가하는 방법
declar let hasMiddle: boolean;
const firstLast = {first: 'Harry', last: 'Truman'};
const president = {...firstAlst, ...(hasMiddle ? { middle: 'S' } : {})};
const president:{
middle?: string;
first: string;
last: string;
}
interface Coordinate {
x: number;
y: number;
}
interface BoundingBox {
x: [number, number];
y: [number, number];
}
interface Polygon {
exterior: Coordinate[];
holes: Coordinate[][];
bbox?: BoundingBox;
}
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox;
if (polygon.bbox) {
if (pt.x < box.x[0]) {
// ❌ ~~ 객체가 'undefined'일 수 있습니다.
}
}
}
polygon.bbox를 별도의 box라는 별칭을 만들었기 때문에 제어 흐름 분석을 방해
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox;
if (box) {
if (pt.x < box.x[0]) {
// 정상
}
}
}
에러는 해결했지만 box와 bbox는 같은 값인데 다른 이름을 사용하여 코드를 읽는 사람에게 문제가 될 수 있다.
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const { bbox } = polygon;
if (bbox) {
const { x, y } = bbox;
if (pt.x < x[0]) {
// 정상
}
}
}
👍 객체 비구조화를 이용하면 보다 간결한 문법으로 일관된 이름을 사용할 수 있다.
❗ 주의할 점 : x, y가 선택적 속성일 경우에 속성 체크가 필요하다. (null 체크)