[英]TypeScript - Restrict keys of an abstract member based on generic enum or string list
我什至不知道如何找到正確的詞來解釋我正在嘗試做的事情,所以如果我混淆了術語,請道歉。 我想要做的是將對象/接口的鍵限制為屬於枚舉(或 string[] 列表)的鍵列表。 這在基本層面已經是可能的,如下所示:
enum RestrictKeysTo {
name = 'name',
age = 'age'
}
type Person = {
[K in RestrictKeysTo]?: string | number;
}
const phil: Person = {
// Allowed
age: 24,
name: 'Phillip'
// Throws error
occupation: 'developer'
};
這很棒! 但是,我想以更靈活的方式使用它,其中可以通過接口或類傳入已接受鍵的列表,並且可以使用該列表來限制其他地方的鍵。 我正在尋找的具體最終目標是這樣的:
abstract class BaseClass {
public abstract keys: string[];
// How do I restrict ages to be keyed by keys?
public abstract ages: //...
}
class Final extends BaseClass {
public keys = ['joe', 'ralph'];
public ages = {
joe: 24,
ralph: 50
}
}
我猜如果存在解決方案,它涉及將泛型傳遞給基類,但我無法確定具體細節......例如,這是我似乎得到的最接近的(工作):
abstract class BaseClass<T extends string> {
public abstract ages: {
[K in T]: number
}
}
type Keys = 'joe' | 'ralph' | 'betty'
class Final extends BaseClass<Keys> {
public ages = {
joe: 24,
ralph: 50,
betty: 32
};
}
但是,這不太理想,因為我希望能夠跨基類重用密鑰並填充(抽象)方法。 我以為我會通過構造函數傳遞它,但我遇到了兩個問題:
這是一個非功能性示例,我可能很接近:
type KeyArr<T extends string> = {
[K in T]?: keyof T;
}
abstract class BaseClass<T extends string> {
public keys: KeyArr<T>;
constructor(keys: KeyArr<T>) {
this.keys = keys;
}
public abstract ages: {
[K in T]?: number
}
}
type Keys = 'joe' | 'ralph' | 'betty'
class Final extends BaseClass<Keys> {
constructor() {
// This should ideally throw an error, since it is missing 'betty', and 'joe' does not belong to Keys
// Not working - says nothing in common with KeyArr
super(['joe', 'ralph', 'joe'])
}
public ages = {
joe: 24,
ralph: 50,
betty: 24
};
}
老實說,出於某種原因,我對 TS 泛型有一個心理障礙——所以很可能我在這里遺漏了一些基本的東西。 任何幫助表示贊賞!
使用您的代碼顯示以下解決方案可以正常工作,並且應該符合您的要求。
abstract class BaseClass<T extends string> {
public abstract keys: T[];
public abstract ages: {
[K in T]?: number
}
}
enum Keys {
Joe = "Joe",
Ralph = "Ralph",
Betty = "Betty"
}
class Final extends BaseClass<Keys> {
public keys = [Keys.Betty, Keys.Joe, Keys.Ralph];
public ages = {
[Keys.Betty]: 24
};
}
const final = new Final();
const a = final.ages[Keys.Betty] // number
如您所見,我做了幾件事:1. 我使用的是枚舉而不是字符串文字 2. 我刪除了構造函數,因為在最終類中您沒有使用它的參數 3. 我將鍵的類型更改為簡單的T[]
現在為什么我做了這些改變。
字符串文字的問題是 - 在擴展類傳遞 ['betty', 'ralph'] 時將其推斷為 string[],為了修復它,我需要做 - ['betty' as const, 'ralph' as const]。 由於我想避免這種情況,我選擇了枚舉。
我刪除了構造函數,因為您的實現表明您想要在類內部定義鍵,而不是從外部傳遞它們。
KeyArr
與簡單的T[]
沒有區別
下面使用字符串文字形式比較實現:
abstract class BaseClass<T extends string> {
public abstract keys: T[];
public abstract ages: {
[K in T]?: number
}
}
type Keys = 'joe' | 'ralph' | 'betty'
class Final extends BaseClass<Keys> {
public keys = ['betty' as const, 'joe' as const]; // as const because inference showing string
public ages = {
'betty': 24
};
}
const final = new Final();
const a = final.ages.betty // number
兩種解決方案看起來都有效。
我給出的兩個解決方案都允許放置並非所有鍵的數組。 這樣做是因為您的ages
類型不需要每個鍵的值 - {[K in T]?: number}
表示它的部分記錄。 在我看來,如果keys
不是詳盡無遺而年齡是詳盡無遺的,那么整體就會有意義。 考慮:
abstract class BaseClass<T extends string> {
public abstract keys: T[];
public abstract ages: {
[K in T]: number // pay attention removed ?
}
}
type Keys = 'joe' | 'ralph' | 'betty'
class Final extends BaseClass<Keys> {
public keys = ['betty' as const, 'joe' as const]; // as const because inference showing string
public ages = {
'betty': 24 // error total record of all keys needs to be provided
};
}
這種差異意味着在我們的 Final 類中,我們可以定義我們需要為年齡提供哪些鍵。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.