簡體   English   中英

Typescript 根據同一對象中的屬性推斷函數的對象鍵

[英]Typescript infer object keys for function based on properties in the same object

編輯:重新編寫問題以使其更加清晰並添加了一些其他示例。

我想創建一個大對象(這是底層系統的要求),我想將該對象的兩個元素之間的一種關系建模為 TypeScript 接口/類型,由 TypeScript 引擎自動推斷。

我的對象看起來像這樣:

{
  selection: {
    name: "",
    email: ""
  },
  handler: (values) => {
    // Do something
  }
}

我想要的是定義一種類型,以便將selection對象中定義的鍵(在本例中為nameemail ,但可能更多)被推斷為values對象上的鍵。

例如:

{
  selection: {
    name: "",
    email: ""
  },
  handler: (values) => {
    values.name // Valid
    values.email // Valid
    values.foobar // Not valid
  }
}

// or

{
  selection: {{
    name: "",
    email: ""
    address: "",
    role: ""
  },
  handler: (values) => {
    values.name // Valid
    values.email // Valid
    values.address // Valid
    values.role // Valid
    values.foobar // Not valid
  }
}

使用這種類型:

interface HandlerObject<TSelection extends { [key: string]: string }> {
  selection: TSelection
  handler?: (selection: { [key in keyof TSelection]: any }) => any
}

只要我首先顯式設置通用TSelection ,我就可以輸入對象:

interface Selection {
  name: string
  email: string
}

const obj: HandlerObject<Selection> = {
  selection: {
    name: "",
    email: ""
  },
  handler: (values) => {
    values.name // Valid
    values.email // Valid
    values.foobar // Not valid
  }
}

然而,對我來說,TypeScript似乎應該能夠根據我在selection屬性中輸入的內容來推斷TSelection本身。 雖然我當然可能是錯的。 我的理論是我應該能夠做到這一點:

const obj: InferredHandlerObject = {
  selection: {
    name: "",
    email: ""
  },
  handler: (values) => {
    values.name // Valid
    values.email // Valid
    values.foobar // Not valid
  }
}

並且仍然以某種方式對nameemail進行正確的類型推斷。 這是可能的還是我需要明確輸入選擇?


原始問題

// SomeType is just a placeholder. This is where I want the proper type to go.
const obj: AType = {
  selection: {
    name: "something", // The value here is irrelevant. The key is important
    email: "something elese" // The value here is irrelevant. The key is important
  },
  handler: (selection) => {
    selection.name // selection only contains keys from the selection object defined above
  }
}

有沒有辦法使用 Typescript 以這樣的方式鍵入它,即 selection argument始終包含與selection屬性中定義的鍵相同的鍵? 參數中鍵的值也不應該相同。

我試過這個:

interface HandlerObject<TSelection extends { [key: string]: string }> {
    selection: TSelection
    handler?: (selection: { [key in keyof TSelection]: any }) => any
}

但它要求我明確指定泛型。

在此處輸入圖片說明

我希望它自動根據selection屬性推斷TSelection

您已經說過SomeType是“屬性類型應該去哪里的占位符”。 如果obj是一次性的,則根本不需要類型; TypeScript 將從對象初始值設定項中推斷出它。 但是我們需要將它分開一點,否則你會試圖從它自己的初始化器中引用它,而 TypeScript 不會讓你這樣做。 您會得到“'obj' 隱式具有類型'any',因為它沒有類型注釋並且在其自己的初始化程序中被直接或間接引用。(7022)”操場鏈接)。

下面是我們如何在類型聲明中使用typeof來做到這一點,通過將它分開一點:

const initialSelection = {
    name: "select.name",
    email: "select.email"
};
const obj = {
    selection: initialSelection,
    handler: (selection: typeof initialSelection) => {
        selection.name // This works
    }
};

游樂場鏈接

如果您想避免在該范圍內使用initialSelection ,您可以將其包裝在一個函數中:

const obj = (() => {
    const initialSelection = {
        name: "select.name",
        email: "select.email"
    };
    return {
        selection: initialSelection,
        handler: (selection: typeof initialSelection) => {
            selection.name // This works
        }
    };
});

游樂場鏈接

如果您堅持將所有內容都放在一個對象中,則需要像這樣定義SomeType

interface SomeType {
  selection: {
    name: string;
    email: string;
  },
  handler: (selection: SomeType['selection']) => void;
}

const obj: SomeType = {
  selection: {
    name: "select.name",
    email: "select.email"
  },
  handler: (param) => {
    param.name;
    param.email;
  }
}

操場

不完全確定你在追求什么,但我假設你知道什么會進入“選擇”,它可能因情況而異,你想確保處理程序只使用現有的屬性。

interface SomeType<T> {
    selection: T,
    handler: (selection: T) => void
}

type User {
    name: string;
    email: string;
}


const obj: SomeType<User> = {
  selection: {
    name: "select.name",
    email: "select.email"
  },
  handler: (selection) => {
    selection.name // selection only contains keys from the selection object defined above
  }
}

無論出於何種原因,泛型類型推斷對函數的操作與對類型的操作不同。

這是解決方案:

function isObj<O>(obj: { selection: O; handler: (selection: { [key in keyof O]: any }) => any }) {
  return obj;
}

const obj = isObj({
  selection: {
    name: '',
    email: '',
  },
  handler(values) {
    values.name; // Valid
    values.email; // Valid
    values.foobar; // Not valid
  },
});
obj;

暫無
暫無

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

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