網站搜尋

類型 - 使用字串存取 TypeScript 的選用物件屬性


我正在一個現有的 JavaScript 代碼庫中工作。有一個類別提供預先定義的函數(例如「copy」、「paste」)作為工具。該類別可以通過「extension」函數實例化,允許使用者註冊其他工具函數以便日後使用。

這段程式碼沒有使用類型,因此我正在嘗試為函式簽名添加類型。但我在取得工具函式的函式(get(name))上遇到很多麻煩。程式碼的簡化版本(包含我嘗試添加的類型)如下:

class Operation {
    private extensions: {[key: string]: () => void}
    constructor(extensions: {[key: string]: () => void}) {
        this.extensions = extensions;
    }
    get(name: string): () => void {
        if (this[name]) {
            return this[name].bind(this)
        }
        if (this.extensions[name]) {
            return this.extensions[name].bind(this)
        }
        // default to copy
        return this.copy.bind(this);
    }
    copy() {
        console.log('copied');
    }
    paste() {
        console.log('pasted');
    }
}
const operation = new Operation({'cut': () => { console.log('cut'); }});
operation.get('cut')();

這失敗是因為 this[name]:"元素隱式具有 'any' 類型,因為類型 'Operation' 沒有索引簽名 ts(7053)"。

由於這個函數是用來接受任意字串(因為有覆寫),我認為我無法避免將函數輸入類型寫成 get(name: string)。我無法從 TypeScript 文件中弄清楚如何使用條件類型,例如 get(name: string | keyof Operation),而且我也不確定那是不是正確的解決方案。

在嚴格類型下,對於 get(name) 函數,給定 name 不保證是 Operation 的屬性,重構與定義其類型的最佳方式是什麼?

答案#1

檢查(在 JavaScript 中,而不是 TypeScript)被訪問的鍵是否是你想允許的類上直接擁有的鍵——例如 copy 或 paste。然後,TS 將自動推斷這種訪問是被允許的。

get(name: string): () => void {
    if (name === 'copy' || name === 'paste') {
        return this[name].bind(this)
    }
    if (this.extensions[name]) {
        return this.extensions[name].bind(this)
    }
    // default to copy
    return this.copy.bind(this);
}