[英]Typescript infer object keys for function based on properties in the same object
編輯:重新編寫問題以使其更加清晰並添加了一些其他示例。
我想創建一個大對象(這是底層系統的要求),我想將該對象的兩個元素之間的一種關系建模為 TypeScript 接口/類型,由 TypeScript 引擎自動推斷。
我的對象看起來像這樣:
{
selection: {
name: "",
email: ""
},
handler: (values) => {
// Do something
}
}
我想要的是定義一種類型,以便將selection
對象中定義的鍵(在本例中為name
和email
,但可能更多)被推斷為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
}
}
並且仍然以某種方式對name
和email
進行正確的類型推斷。 這是可能的還是我需要明確輸入選擇?
原始問題
// 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.