p.101
101쪽 배열 요소 조회하는거 해보니까 안됨. GPT한테 물어보니까 이렇게 하라고 해서 했더니 됐음
const PromotionList = [
{ type: "product", name: "chicken" },
{ type: "product", name: "pizza" },
{ type: "card", name: "cheer-up" },
];
// T는 반드시 배열 타입이어야 합니다.
type ElementOf<T extends Array<any>> = T[number];
// type ElementOf<T> = (typeof T)[number];
type PromotionItemType = ElementOf<typeof PromotionList>;
p.113
- 코드가 읽히지가 않아서 주석을 달았습니다.
type ErrorRecord<Key extends string> = Exclude<Key, ErrorCodeType> extends never
? Partial<Record<Key, boolean>>
: never;
1. Exclude?
타입스크립트에서는 조건부 형식으로 타입을 정의할 수 있는데요, 아래와 같은 형태를 취합니다.
T extends U ? X : Y
조건식 결과에 따라 X가 될 수도, Y가 될 수도 있습니다.
타입스크립트는 용도에 맞게 조건부 타입을 활용한 새로운 타입들을 미리 정의해 두었고, 이것을 Predefined conditional types 라고 하며 그 중 하나가 Exclude 입니다.
실제 Exclude는 아래와 같이 정의되어 있습니다.
type Exclude<T, U> = T extends U ? never : T;
즉, T에 오는 타입들 중 U에 오는 것들은 제외하겠다는 의미입니다.
type Fruit = "cherry" | "banana" | "strawberry" | "lemon";
type RedFruit = Exclude<Fruit, "banana" | "lemon">;
// type RedFruit = "cherry" | "strawberry" 와 같다.
interface Player {
flyingPet: string;
matchRank: string;
matchWin: string;
matchTime: string;
matchRetired: string;
rankinggrade2: string;
}
type Match = Exclude<keyof Player, "flyingPet">;
// type Match = "matchRank" | "matchWin" | "matchTime" | "matchRetired" | "rankinggrade2" 와 같다.
2. Partial
-특정 타입의 부분 집합을 만족하는 타입을 정의할 수 있습니다.
interface Address {
email: string;
address: string;
}
type MyEmail = Partial<Address>;
const me: MyEmail = {}; // 가능
const you: MyEmail = { email: "noh5524@gmail.com" }; // 가능
const all: MyEmail = { email: "noh5524@gmail.com", address: "secho" }; // 가능
interface Product {
id: number;
name: string;
price: number;
brand: string;
stock: number;
}
// Partial - 상품의 정보를 업데이트 (put) 함수 -> id, name 등등 어떤 것이든 인자로 들어올수있다
// 인자에 type으로 Product를 넣으면 모든 정보를 다 넣어야함
// 그게 싫으면
interface UpdateProduct {
id?: number;
name?: string;
price?: number;
brand?: string;
stock?: number;
}
// 위와 같이 정의한다.
// 그러나 같은 인터페이스를 또 정의하는 멍청한 짓을 피하기 위해서 우리는 Partial을 쓴다.
function updateProductItem(prodictItem: Partial<Product>) {
// Partial<Product>이 타입은 UpdateProduct 타입과 동일하다
}
3. Record
- 타입스크립트의 유틸리티 타입 중 하나로, 인덱스 시그니처와 유사한 기능을 한다.
type Score = {
[name: string]: number;
};
// Score와 동일한 역할
type ScoreRecord = Record<string, number>;
let scores: ScoreRecord = {
치즈볼: 100,
초코볼: 200,
};
- Record가 인덱스 시그니처와 다른 점은, Key로 문자열 리터럴을 사용할 수 있다는 것이다.
// 인덱스 시그니처는 key 타입으로 문자열 리터럴 사용 불가
type Score = {
[name: "치즈볼" | "초코볼"]: number;
};
ErrorRecord 타입은 오직 Key가 ErrorCodeType에 완전히 속하는 경우에만 유효한 타입을 생성함. 이 타입은 해당 에러 코드들의 발생 여부를 나타내는 객체를 다루기 위한 타입으로 사용하고 그 외의 경우에는 타입이 never이 되어, 잘못된 키 사용을 타입 시스템 수준에서 막는다.
-
Exclude
<T, U>
유틸리티 타입은 TypeScript에서 조건부 타입(Conditional Types)을 사용하여 정의됩니다.이 유틸리티 타입의 목적은 타입 T에서 타입 U에 포함된 모든 서브타입을 제거하는 것입니다. 이를 이해하려면 조건부 타입의 작동 방식과 분배적(distributive) 특성을 이해해야 합니다.
-
조건부 타입 (Conditional Type)
조건부 타입은 T extends U ? X : Y 형식을 가집니다. 여기서 T가 U를 상속하거나, U에 할당 가능한 타입이면 결과는 X가 되고, 그렇지 않으면 Y가 됩니다.
-
Exclude의 정의
Exclude`<T, U>`의 정의를 살펴보면, T extends U ? never : T 입니다.
여기서 중요한 것은 조건부 타입이 각 타입에 대해 개별적으로 적용된다는 것입니다. 이것은 조건부 타입이 분배적이라는 의미입니다.
- 분배적 특성 조건부 타입은 유니온 타입(|)을 사용하여 T에 여러 타입이 있을 경우 각 타입에 대해 개별적으로 조건을 적용합니다. 예를 들어 T가 A | B | C이고 U가 B일 경우, Exclude
<T, U>
는 다음과 같이 계산됩니다: A extends B ? never : A → A (A는 B와 다르므로 A를 반환) B extends B ? never : B → never (B는 B와 같으므로 never를 반환) C extends B ? never : C → C (C는 B와 다르므로 C를 반환)
결과적으로 Exclude`<A | B | C, B>`는 A | C가 됩니다.
- 왜 U를 제외한 결과가 되는가?
Exclude
<T, U>
에서 T 타입이 U 타입에 할당 가능하다면, 즉 T가 U의 일부분이라면, 그 결과는 never가 됩니다. never는 TypeScript에서 타입이 존재하지 않음을 나타냅니다. 따라서 이 조건이 참인 경우들을 모두 제외한 나머지 타입들만이 결과로 남게 됩니다.
이렇게 Exclude<T, U>
는 T 유형에서 U 유형에 속하는 모든 서브타입을 제거한 결과를 생성하는 유틸리티 타입입니다. 이는 특정 타입을 제외하고 나머지 타입을 사용하고자 할 때 매우 유용합니다.
Exclude<Key, ErrorCodeType> extends never:
Exclude는 Key에서 ErrorCodeType에 포함된 모든 타입을 제거합니다. extends never는 Exclude의 결과가 never인 경우를 체크합니다. 즉, Key의 모든 요소가 ErrorCodeType에 속하는 경우입니다. 다시 말해, Key가 ErrorCodeType의 하위 집합인 경우에 해당합니다.
결과적 타입
조건이 참인 경우 (Key가 ErrorCodeType의 하위 집합일 때):
Partial<Record<Key, boolean>>
타입을 반환합니다. 이는 Key를 키로 사용하고, 그 값으로 boolean을 갖는 객체이며, 모든 속성은 선택적입니다. 이는 Key로 주어진 에러 코드들의 발생 여부를 추적할 수 있는 객체를 의미합니다. 조건이 거짓인 경우: never를 반환합니다. 이는 Key 중에 ErrorCodeType에 포함되지 않는 어떤 키도 포함하고 있다면, ErrorRecord 타입을 사용할 수 없음을 의미합니다. 사용 예 이를 통해 ErrorRecord 타입은 특정 에러 코드만을 키로 사용하는 객체를 안전하게 정의할 수 있게 해주며, 잘못된 키가 사용되는 것을 컴파일 시간에 잡아낼 수 있습니다. 예를 들어, 다음과 같이 사용할 수 있습니다:
type ErrorCodeType = "Error404" | "Error500";
type ValidErrorRecord = ErrorRecord<"Error404" | "Error500">; // 유효함: Partial<Record<'Error404' | 'Error500', boolean>>
type InvalidErrorRecord = ErrorRecord<"Error404" | "Error500" | "Error408">; // 무효함: never
- 위 코드에서 ValidErrorRecord는 유효한 타입이 되어, Error404와 Error500 각각의 발생 여부를 boolean 값으로 가질 수 있는 선택적 속성을 가진 객체를 정의할 수 있습니다. 반면, InvalidErrorRecord는 Error408이 ErrorCodeType에 포함되지 않으므로, 타입이 never가 되어 사용할 수 없습니다.
p.123 (유니온 교차 )
오히려 책 설명이 저를 더 헷갈리게 해서 다시 코드로 찍어봤습니다. 교집합, 합집합 개념을 오히려 더 헷갈리게 적어놓은 느낌..?
interface A {
orderId: string;
time: number;
price: number;
}
interface B {
orderId: string;
time: number;
distance: string;
}
type AB = A & B;
function ABFunc(params: AB) {
console.log(params);
// orderId,time,price,distance
}
function unionAB(params: A | B) {
console.log(params);
// orderId, timeId
}
p.138 is 타입 가드
최근에 저도 예시코드처럼 true일 때 push를 하는 코드를 작성하다가 타입스크립트 에러가 났던 적이 있던거 같은데 어찌어찌 해결은 했지만 (뭐였는지 기억이 잘안남) 근데 is 타입 가드를 사용하는 방법을 배워서 다음에는 써먹어봐야겠다.
p.147
예시코드를 보고 쉽게 적용해볼 수 있을거 같아서 해보려고 했으나 잘 안됨.
예시코드에 나오는 exhaustiveCheck 부분은 분기처리를 까먹는걸 방지해준다는거 같은데 실제로 써보면 책에 주석처리된 것처럼 에러가 빨간줄로 나옴. 그거 냅두고 그냥 사용하라는것인지..? 구글링 다시해보니 switch문에 적용한 예시코드가 있길래
const reducer: React.Reducer<State, Action> = (state, action) => {
if (!action) {
throw new Error("액션이 없는디?");
}
const { className } = state;
const { type, payload } = action;
switch (type) {
case "2xl":
return { className: payload };
case "xl":
return { className: payload };
case "lg":
return { className: payload };
case "md":
return { className: payload };
case "sm":
return { className: payload };
default:
exhaustiveCheck(type);
return { className };
}
};
이렇게 적용 해봄 이렇게 하라는거 맞는지??
책과 관련없는 이야기거리 작성해봄
전역 상태 관리 vs 지역 상태 관리 (useState?)