簡體   English   中英

TypeScript - 基於通用枚舉或字符串列表限制抽象成員的鍵

[英]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[]

現在為什么我做了這些改變。

  1. 字符串文字的問題是 - 在擴展類傳遞 ['betty', 'ralph'] 時將其推斷為 string[],為了修復它,我需要做 - ['betty' as const, 'ralph' as const]。 由於我想避免這種情況,我選擇了枚舉。

  2. 我刪除了構造函數,因為您的實現表明您想要在類內部定義鍵,而不是從外部傳遞它們。

  3. 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.

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