Coding-Village-Protector / woowahan-ts

[우아한 타입스크립트 with 리액트] 북 스터디 📚
14 stars 2 forks source link

4.3.2_식별할 수 있는 유니온을 사용하는 이유는 무엇일까요 #11

Closed sryung1225 closed 9 months ago

sryung1225 commented 9 months ago

📝 p141

❓다음은 식별할 수 있는 유니온(Discriminated Unions)을 이용하여 타입을 좁힌 예제입니다.

type IPhone = {
    name: "아이폰",
    version: number,
}
type AirPod = {
    name: "에어팟",
    version: number,
    noisecancel: boolean,
}

type AppleProductType = IPhone | AirPod;
const products: AppleProductType[] = [
    { name: "아이폰", version: 15 },
    { name: "아이폰", version: 15, noisecancel: true }, // 🚨 error
];

위 예제는 타입 좁히기를 무사히 성공하여 products 배열의 요소 중 두번째는 에러를 출력하도록 만들었습니다.
그렇다면 (1) 왜 타입 에러를 갖게 하는 것이 좋은 코드인지에 대한 설명과 (사실 이 부분은 복습이여요) (2) 위 코드가 식별할 수 유니온을 사용하기 이전에 아무 타입 에러도 뱉지 않는 코드는 어땠을 것 같은지 역으로 추론해주세요.

lulla-by commented 9 months ago

(1) 타입 에러를 발생시켠 처음에 기대한 객체의 타입에서 타입간 호환으로 인해 발생할 수 있는 문제를 확인할 수 있습니다.

(2) IPhone과 AirPot의 필드를 모두 가지는 객체가 있을 경우 에러를 발생시켜야 할 것 같지만 자바스크립트는 덕타이핑 기반이기 때문에 서로 호환이 되어 에러가 발생하지 않습니다.


type IPhone = {
    version: number,
    chargeType: string
}
type AirPod = {
    version: number,
    noisecancel: boolean,
}

type AppleProductType = IPhone | AirPod;
const products: AppleProductType[] = [
    {version: 15, chargeType: "C"},
    {version: 3,noisecancel:true },
    {version: 15,chargeType: "C", noisecancel:true }, // 에러가 발생하지 않음
];
eeeyooon commented 9 months ago

(1) Ipone과 AirPod은 타입별로 각자만의 고유한 필드를 가진다. Ipone의 chargeType과 feature, AirPod의 noisecancel이 바로 그 고유한 필드이다. Ipone과 AirPod의 유니온 타입인 AppleProductType의 원소를 갖는 배열 products는 Ipone의 고유 필드와 AirPod의 고유 필드를 모두 가지는 객체에 대해서는 타입 에러를 뱉어야 한다. 하지만 식별할 수 있는 유니온을 사용하지 않으면 위와 같은 상황에서도 별도의 타입 에러를 뱉지 않는다. 이런 상황에서 에러가 발생하지 않는다면 무수한 에러 객체가 생겨도 개발 과정에서 발견하지 못할 위험성이 커진다. 예제 코드처럼 식별할 수 있는 유니온을 추가하여 타입마다 구분할 수 있는 판별자를 달아주면, 정확하지 않은 에러 객체에 대해선 타입 에러가 발생하여 발견하고 처리할 수 있다.

(2)

function call() {
  console.log("여보세요?");
}

type IPhone = {
    version: number,
    chargeType: string,
    feature: () => void,
}
type AirPod = {
    version: number,
    noisecancel: boolean,
}

type AppleProductType = IPhone | AirPod;
const products: AppleProductType[] = [
    { version: 15, chargeType: "C", feature: call },
    { version: 15, feature: call, chargeType: "C", noisecancel: true }, // 에러가 발생하지 않음.
];

다른 타입의 고유 필드까지 가지고 있는 에러 객체가 발생해도, 타입 에러가 발생하지 않아 에러 객체가 그대로 존재하는 문제가 발생할 수 있다. 이러한 에러 객체의 존재는 예상치 못한 문제의 원인이 되는 등 위험성을 가지고 있다.

Stilllee commented 9 months ago

타입 에러를 갖게함으로써 코드의 안정성을 보장하고 잘못된 타입을 예방하여 버그를 줄이는 데 도움이 된다. 이는 개발자가 코드의 의도를 명확하게 표현하고, 예상치 못한 오류를 방지할 수 있게 해준다.

type IPhone = {
    version: number,
}
type AirPod = {
    version: number,
    noisecancel: boolean,
}

type AppleProductType = IPhone | AirPod;
const products: AppleProductType[] = [
    { version: 15 },
    { version: 15, noisecancel: true }, // 에러발생 x
];
sryung1225 commented 9 months ago

자바스크립트의 경우 덕 타입 언어이기 때문에 타입 에러가 우선 보여지지 않는 문제가 있습니다. 이 경우 개발자는 원치않은 에러를 뒤늦게 발견할 수 있기 때문에 개발자에게 에러를 미리 보여주도록한 코드가 더 안정적인 코드라고 할 수 있습니다.
추론할 수 있는 과거 코드의 경우의 수는 많고 다른 분들이 대답해준 것 역시 정답입니다. 제가 준비한 답안은 식별할 수 있는 유니온의 판별자 선정 과정을 거치기 전으로 코드를 추론하는 것입니다.

/* 판별자로 name을 선정 후 */
type IPhone = {
    name: "아이폰",
    version: number,
}
type AirPod = {
    name: "에어팟",
    version: number,
    noisecancel: boolean,
}

/* 판별자로 name을 선정 전 */
type IPhone = {
    name: string,
    version: number,
}
type AirPod = {
    name: string,
    version: number,
    noisecancel: boolean,
}

두 타입 IPhoneAirpod 을 구분할 수 있도록 하기 위해서는 타입마다 구분할 수 있는 판별자가 필요하고 문제에서는 이 판별자가 name 입니다. 이 판별자로 인해 서로 포함 관계를 벗어남으로서 좁혀진 타입의 에러를 미리 확인하고 사용할 수 있게 됩니다.