이번에는 Type Script의 infer를 잘 이해해주세요.

19330 단어 TypeScripttech

어렵다


사용 기회는 적지만 개인은 React Query의 키 관리 등에서 이런 사용법을 자주 사용한다.
적극적이지 못하다infer는이해가 안되기 때문에하나하나분해해가며깊게이해하려고 하는것 같아요.
export const QUERY_KEYS = ["users", "post", "comments"] as const;
export type Unpacked<T> = T extends { [K in keyof T]: infer U } ? U : never;
export type QueryKeysTypes = Unpacked<typeof QUERY_KEYS>;
https://zenn.dev/brachio_takumi/articles/d6ddb655c4c30f908c67

const 결단


as const
이것은 const 결단이다.이를 설명하기 전에 위딩 리터럴 Types와 논 위딩 리터럴 Types를 전제로 이해할 필요가 있다.
const로 변수를 정의할 때 hoge 의 유형은 "HOGE" 이다.
const hoge = "HOGE";
// type hoge: "HOGE"
let sameHoge = hoge;
// type sameHoge: string
다른 변수에 대입하면sameHoge: string.결론적으로 변수를 정의한 경우 기본값은 Widening Literal Types입니다.
원래는 const로 형 정의를 내리면 Literal Types였지만, 이 안에는 상기 위드닝 Literal Types와 NonWidening Literal Types 두 가지가 은근히 존재한다.
스트링과'허지'의 한정형이 재대입을 통해 스트링을 확대한 것으로 보인다.반면 논위딩 리터럴 티페스는 아무래도 커지지 않는 리터럴 티페스를 뜻한다.
그렇다면 어떻게 하면 논위딩 리터럴 티페스로 만들 수 있을지에 대해 콘스트리졸브를 사용하면 변수를 선언하면서 유형의 확대를 억제할 수 있다.
const hoge = "HOGE" as const;
let sameHoge = hoge;
// type sameHoge: "HOGE"

// また他のオブジェクトに代入をしてもNonWidening Literal Typesであることがわかる
const obj = {
  hoge, // "HOGE"
};

obj.hoge = "fuga"; // おこ
const결단은 당연히 배열과 대상에 적용되고readonly도 수여된다.따라서 그룹을 다시 대체할 수 없습니다.
const hoge = ["HOGE"] as const;
// type  hoge: readonly ["HOGE"]
hoge[1] = "huga"; // Tuple type 'readonly ["HOGE"]' of length '1' has no element at index '1'
readonly가 부여돼 재대입이 불가능해지자 리액트 프로젝트에서 더 적극적으로 활용하고자 한다.

Conditional Types


Condiional Types는 세 연산자와 동일하게 쓸 수 있는 유형 정의의 조건 브랜치입니다.
쉽게 말해 T형이 땡땡이라면 이런 조건을 틀에 넣을 수 있다.
type IsNumber<T> = T extends number ? true : false;

type Hoge = IsNumber<"a">; // false
type Fuga = IsNumber<1>; // true
type Foo = IsNumber<boolean>; // false

키오브와 조합하면 이런 것도 할 수 있어요.


대상에 속성이 있으면, 대상의 값을 되돌려주고, 없으면 type 오류를 되돌려줍니다.
따라서 대상에 따라 유연하게 대응할 수 있고 타자를 방지할 수 있다.
const getValue = <T, U extends keyof T>(value: T, key: U): T[U] => {
  return value[key];
};

const dog = {
  name: "Taro",
  age: 10,
};

const cat = {
  name: "Jiro",
  age: 3,
  type: "hoge",
};

getValue(dog, "name");
// function getValue<{
//     name: string;
//     age: number;
// }, "name">(value: {
//     name: string;
//     age: number;
// }, key: "name"): string

getValue(dog, "type"); // Argument of type '"type"' is not assignable to parameter of type '"name" | "age"'.

getValue(cat, "type");

// function getValue<{
//     name: string;
//     age: number;
//     type: string;
// }, "type">(value: {
//     name: string;
//     age: number;
//     type: string;
// }, key: "type"): string

infer


직역하면 추론의 의미로 한마디로 형식을 추단해 낼 수 있다.
예를 들어 T에 id 속성이 있으면 id 유형을 반환합니다.
id 속성이 없으면never로 되돌아갈 수 있습니다.
type Id<T> = T extends { id: infer U } ? U : never;
또한 infer를 사용하는 장소는 condiional types의 extends의 조건 부분에 한정되어 있기 때문에 사용 방법으로 conditional types와 조합하여 사용한다.

원래 코드를 풀다


그럼 최초의 코드를 풀자.
export const QUERY_KEYS = ["users", "post", "comments"] as const;
export type Unpacked<T> = T extends { [K in keyof T]: infer U } ? U : never;
export type QueryKeysTypes = Unpacked<typeof QUERY_KEYS>;
먼저 const 할당을 통해 스토리지를 Non Widening Literal Types로 설정합니다.
이렇게 하면QUERY_KEYS: readonly ["users", "post", "comments"].
export const QUERY_KEYS = ["users", "post", "comments"] as const;
const 결단을 내리지 않았다면 단순했을 뿐string[].
export const QUERY_KEYS = ["users", "post", "comments"];
// QUERY_KEYS: string[]
다음은 Unpacked 무엇을 하는지 봅시다.
전체 이미지로서 T[K in keyof T] 속성이 있으면 해당 유형이 반환됩니다.
export type Unpacked<T> = T extends { [K in keyof T]: infer U } ? U : never;
그럼 이번 T는 뭘까요["users", "post", "comments"][K in keyof T]
[0]: 그럼 inferU는 "users"
[1]: 그러면 inferU는 "post"입니다.
[2]: 그럼 inferU는 "comments"
지렛대가 되다.또 네버는'있을 수 없는 유형'이지만 이번 코드의 특성상 [K in keyof T]는 존재하지 않기 때문에 네버에 들어가지 않는다.
마지막으로, 변수 QUERYKEYS 유형에서 정렬된 유형을 제거합니다.
export type QueryKeysTypes = Unpacked<typeof QUERY_KEYS>;
따라서QueryKeysTypes의 유형은 다음과 같다.
type QueryKeysTypes = "users" | "post" | "comments";

총결산


infer를 사용하면 약간 빙빙 도는 유형 정의가 있을 수 있지만 실제 코드나 사용된 배열로 유형을 정의하는 것이 좋습니다.배열과 제작 유형을 동시에 제작할 수 있는 느낌.
많이 이해했으니까 업무적으로 적극적으로 활용하고 싶어요.

좋은 웹페이지 즐겨찾기