簡體   English   中英

打字稿:通用到特定類型

[英]Typescript: Generic to specific type

我有一個通用類型

type GenericType = {
  [key: string]: {
    prop1: string,
    prop2?: string,
    prop3?: number,
  },
};

我使用通用類型來幫助構建/類型檢查我創建的新對象。

const Obj1: GenericType = {
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
};

有用。 但是,當我使用新對象時,vscode / typescript 不會顯示Obj1的鍵或道具,而不會Obj1刪除GenericType 我也可以“擴展”類型,但這是代碼重復。

Obj1.???

有沒有辦法在保留 GenericType 的同時從我創建的新對象中訪問更具體的道具?


更新 1

我希望 vscode/typescript 顯示/驗證

Obj1.key1.prop1
Obj1.key2.prop1

和錯誤如果

Obj1.key1.prop2
Obj1.key2.prop3
Obj1.key2.prop321

我看到的用例是,您希望GenericType將任何類型的字符串作為鍵,但仍想確切地確定這些鍵的值在您聲明它們的地方可以是什么。

在這種情況下,您可以使用Record類型將Obj1允許的鍵限制為您指定的鍵。

type GenericType<K extends string> = Record<K, {
  prop1: string,
  prop2?: string,
  prop3?: number,
}>

然后在定義Obj1您可以通過將允許的鍵的並集設置為第一個類型參數來指定允許的鍵應該是什么。

const Obj1: GenericType<"key1" | "key2"> = {
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
};

TypeScript 現在可以讓您以完全類型安全的方式訪問key1key2

Obj1.key1
// (property) key1: {
//     prop1: string;
//     prop2?: string | undefined;
//     prop3?: number | undefined;
// }

編輯

根據 OP 的評論,聽起來他寧願不指定所有鍵名,也不想手動檢查是否存在可選字段。

在確保您聲明的對象與GenericType接口的約束匹配的同時,我能想到的最好方法是執行以下操作。

首先,您需要此實用程序類型:

type Constraint<T> = T extends Record<string, {
  prop1: string,
  prop2?: string,
  prop3?: number,
}> ? T : never

如果T與約束不匹配,則這將never不會返回,否則僅返回T

現在你聲明了你真正想要的普通對象。 沒有類型注釋。

const CorrectObj = {
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
};

然后將此對象字面量分配給另一個變量,但聲明新變量的類型必須為Constraint<typeof CorrectObj>

const CheckedObj: Constraint<typeof CorrectObj> = CorrectObj

如果CorrectObj與約束匹配,則CheckedObj將是包含所有可用字段的CorrectObj的簡單副本。

但是,如果對象字面量與約束不匹配,則在嘗試將 CheckedBadObj 分配給字面量時會出現類型錯誤:

const BadObj = {
  key1: {
    progfdgp1: "hi",
  },
  key2: {
    prop1: "bye",
    prdfgop2: "sup",
  },
};

const CheckedBadObj: Constraint<typeof BadObj> = BadObj
//    ^^^^^^^^^^^^^
// Type '{ key1: { progfdgp1: string; }; key2: { prop1: string; prdfgop2: string; }; }' is not assignable to type 'never'. (2322)

解釋是當T不匹配時Constraint<T>never ,但您仍在嘗試為CheckedBadObj分配一個非never值,從而導致類型沖突!

這在聲明每個對象字面量的兩個實例時涉及到一些重復,但這是唯一的方法

  1. 讓 TypeScript確切知道對象上存在哪些字段,包括所有子對象,同時仍然
  2. 檢查您的“通用”類型的值是否與您的約束匹配

您可以在操場上嘗試這種方法。

假設您的目標是讓key1key2顯示在Obj1的自動完成菜單中,但仍然可以稍后設置其他鍵,這里是一個可能的解決方案:

type GenericType = {
  [key: string]: {
    prop1: string,
    prop2?: string,
    prop3?: number,
  };
};

const generify = <T extends GenericType>(obj: T): T & GenericType => obj;

const Obj1 = generify({
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
});

我現在想不出一個更簡單的解決方案,它可以為Obj1提供GenericType和僅包含key1key2屬性的特定類型之間相同的交集類型。

使用一個通用的實用函數( enforce

  • 使用通用約束( extends )確保對象匹配GenericType
  • 並返回傳入對象的類型以進行類型推斷。

代碼:

type GenericType = {
  [key: string]: {
    prop1: string,
    prop2?: string,
    prop3?: number,
  };
};

const enforce = <T extends GenericType>(obj: T): T => obj;

工作解決方案:

type GenericType = {
  [key: string]: {
    prop1: string,
    prop2?: string,
    prop3?: number,
  };
};

const enforce = <T extends GenericType>(obj: T): T => obj;

const Obj1 = enforce({
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
});


Obj1.key1.prop1; // Ok
Obj1.key2.prop1; // Ok

/** 
 * ERROR: does not match passed in object
 */
Obj1.key1.prop2 // Error 
Obj1.key2.prop3 // Error
Obj1.key2.prop321 // Error

Obj1.key3; // Error 

/**
 * ERRORS: Does not match GenericType
 */
const Obj2 = enforce({
  key1: { // Error 
  }
});
const Obj3 = enforce({
  key1: {
    prop1: 123, // Error
  }
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM