타입 좁히기( 타입스크립트 핸드북 )
function padLeft(padding: number | string, input: string) {
return " ".repeat(padding) + input;
// Argument of type 'string | number' is not assignable to parameter of type 'number'. Type 'string' is not assignable to type 'number'.
이 코드는 string|number 타입을 repeat의 인자로 넘기려고 해서 문제가 발생한다. repeat는 number 타입만 받을 수 있다.
typeof narrowing
function padLeft(padding: number | string, input: string) {
if (typeof padding === "number") {
return " ".repeat(padding) + input;
}
return padding + input;
}
``;
typeof
를 통해 들어온 padding의 타입을 검사한 후 타입이 number인 경우 repeat를 수행할 수 있다.
typeof 구문은 일반적인 자바스크립트와 같아 보이는데, 타입스크립트는 일반적인 자바스크립트 코드처럼 쉽게 작성할 수 있고 타입 안전성을 확보하는 방향을 목표로 한다.
typeof
는 null을 반환하지 않는다.
function printAll(strs: string | string[] | null) {
if (typeof strs === "object") {
for (const s of strs) {
// 'strs' is possibly 'null'.
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
} else {
// do nothing
}
}
null의 타입도 object기 때문에 위의 예제에서 문제가 발생한다.
function printAll(strs: string | string[] | null) {
if (strs && typeof strs === "object") {
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
}
}
Truthiness narrowing
Truthiness(boolean) 체크를 같이 곁들여서 문제를 해결할 수 있음
function printAll(strs: string | string[] | null) {
// !!!!!!!!!!!!!!!!
// DON'T DO THIS!
// KEEP READING
// !!!!!!!!!!!!!!!!
if (strs) {
if (typeof strs === "object") {
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
}
}
}
이건 truthy check 부분이 모든 if문을 감싸는건데 이러면 빈문자열이 동작하지 않으니까 잘못됨 (ts문제라기보다는 자바스크립트에서 할 수 있는 실수...)
Equality narrowing
function printAll(strs: string | string[] | null) {
if (strs !== null) {
if (typeof strs === "object") {
for (const s of strs) {
(parameter) strs: string[]
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
(parameter) strs: string
}
}
}
!==
같은 구문(equality)을 이용해 null을 검사할 수 있다.== null
로 검사하면 null 뿐만이 아니라 잠재적인 undefined도 추려낼 수 있다. == undefined
도 마찬가지이다.
interface Container {
value: number | null | undefined;
}
function multiplyValue(container: Container, factor: number) {
// Remove both 'null' and 'undefined' from the type.
if (container.value != null) {
console.log(container.value); // number
(property) Container.value: number
// Now we can safely multiply 'container.value'.
container.value *= factor;
}
}
느슨한 비교 !=
를 통해 정의되지 않은 값을 배제했다. container.value는 number로 좁혀진다.
in
operator narrowing
객체 프로퍼티의 타입을 좁히는 방법으로 in
을 사용할 수 있다.
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
}
return animal.fly();
}
type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = { swim?: () => void; fly?: () => void };
function move(animal: Fish | Bird | Human) {
if ("swim" in animal) {
animal; // Fish | Human
} else {
animal; // Bird | Human
}
}
선택적 속성은 양쪽 모두에 속할 수 있다.
instanceof
narrowing
타입스크립트는 instanceof
에 해당하는 범위 내로 영역을 좁힌다. new
키워드로 생성할 수 있는 대부분의 값들에 사용할 수 있어 유용하다.
function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString());
(parameter) x: Date
} else {
console.log(x.toUpperCase());
(parameter) x: string
}
}
분리된 유니온
원제는 discriminated union인데 해석이 애매하다..
interface Shape {
kind: "circle" | "square";
radius?: number;
sideLength?: number;
}
이 방법은 circle과 square 유형을 함께 가지고 있다. circle 유형일 때만 radius가 들어올 것이고 square 유형일 때만 sideLength가 들어온다. 그래서 radius와 sideLength는 선택적으로 값이 들어오는 상태다.
function getArea(shape: Shape) {
return Math.PI * shape.radius ** 2;
// 'shape.radius' is possibly 'undefined'.
}
위의 이유로 'shape.radius' is possibly 'undefined'.
오류가 발생하게 된다. 하지만 kind 속성을 기반으로 radius나 sideLength가 있는지 여부를 판단할 방법이 없다. circle과 square의 타입을 구분하는 것이 좋다.
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
각각의 속성(radius, sideLength) 이 선택적이 아닌 필수적인 속성으로 선언되었다.
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
// shape : Circle
}
}
이제 유니온의 속성을 검증하여 타입스크립트가 타입을 좁힐 수 있다.
'공부' 카테고리의 다른 글
[이펙티브TS] DOM 계층구조 이해하기 (아이템 55) (0) | 2023.08.14 |
---|---|
[이펙티브TS] 오버로딩보다 조건부 타입 사용하기(아이템 50) (0) | 2023.08.12 |
[jest/rtl] 강의 기록(msw, 비동기) (0) | 2023.07.10 |
[TS] 이펙티브 타입스크립트 #26 (0) | 2023.04.02 |
[rtl] 테스트 코드 (0) | 2023.03.19 |
댓글