[英]Typescript, automatically infer type from object keys
我想為 Select 組件聲明一個接口,該組件可以是多個或單個項目 select。
interface MySelect<T extends boolean> {
multi: T, // Is this a multiple items select
onChange: (item: T extends true ? string[]: string) => void // the onChange signature differs according to T
}
它有效,但我必須明確設置泛型類型 T:
const mySelect: MySelect<true> = { // Here
multi: true, // And here
onChange: (items) => {}
}
我想知道是否可以讓 TS 自動從“multi”值推斷 T:
const mySelect: MySelect = {
multi: true, // multi is true so T is true
onChange: (items) => {}
}
重要更新:我希望“多個”是可選的(如果鍵丟失或未定義,它將默認為 false)
您已經更新了您的問題,說multi
應該是可選的(默認為 false)。 這排除了有區別的聯合(下面水平線下的先前答案)。
我想我會使用你聯合在一起的兩個接口,以及(如有必要)它們共同擁有的東西的基本接口。 當您需要知道 select 的類型時,您可能需要類型保護功能。
// Things all MySelects have in common (if you have anything other than `onChange`)
interface MySelectBase {
name: string;
}
// A single-select version of MySelect
interface MySingleSelect extends MySelectBase {
multi?: false;
onChange: (item: string) => void;
}
// A multi-select version of MySelect
interface MyMultiSelect extends MySelectBase {
multi: true;
onChange: (items: string[]) => void;
}
// The unified type
type MySelect = MySingleSelect | MyMultiSelect;
// Type guard function to see whether it's a single select
const isSingleSelect = (select: MySelect): select is MySingleSelect => {
return !select.multi; // !undefined and !false are both true
};
// Type guard function to see whether it's a multi select
const isMultiSelect = (select: MySelect): select is MyMultiSelect => {
return !!select.multi; // !!undefined and !!true are both true
};
創建示例:
const single: MySingleSelect = {
name: "some-single-select-field",
onChange : (item) => { console.log(item); }
};
const multi: MyMultiSelect = {
multi: true,
name: "some-multi-select-field",
onChange : (items) => { console.log(items); }
};
使用MySelect
(組合接口)的示例:
const useMySelect = (select: MySelect) => {
// No need for a guard on anything but `onChange`
console.log(select.name);
// `onChange` will be a union type until/unless you use a type guard
const onChange = select.onChange;
// ^^^^^^^^−−−−−−−−−− type is `((item: string) => void) | ((items: string[]) => void)`
if (isSingleSelect(select)) {
// It's a MySingleSelect
const onChange = select.onChange;
// ^^^^^^^^−−−−−−−−−− type is `(item: string) => void`
} else {
// It's a MyMultiSelect
const onChange = select.onChange;
// ^^^^^^^^−−−−−−−−−− type is `(items: string[]) => void`
}
};
對於不需要制作multi
選項的人來說,這是原始答案:
您可以通過將MySelect
聲明為類型的聯合來做到這一點,一個是multi: true
,另一個是multi: false
:
type MySelect =
{
multi: true;
onChange: (items: string[]) => void;
}
|
{
multi: false;
onChange: (item: string) => void;
};
然后你得到:
const mySelect: MySelect = {
multi: true,
onChange: (items) => {}
// ^^^^^^^^−−−−−−−−−−− correctly inferred as (items: string[]) => void
};
這稱為有區別的聯合:由一個(或多個)字段的類型區分(區分)的類型的聯合。
如果您有大量沒有變化的其他屬性,則可以使用交集將它們添加到可區分的聯合中:
type MySelect =
(
{
multi: true;
onChange: (items: string[]) => void;
}
|
{
multi: false;
onChange: (item: string) => void;
}
)
&
{
the: number;
other: string;
properties: string;
};
您可以使用通用標識 function 執行此操作:
function helper<T extends boolean>(obj: MySelect<T>): MySelect<T> {
return obj;
}
// MySelect<true> inferred
const mySelect = helper({
multi: true,
onChange: (items) => {}
});
也就是說,當您的類型參數沒有一組有限的不同選項時,這種技術更有用; 在您的情況下, T extends boolean
可能更好地實現為 TJ Crowder 的答案中的標記聯合類型,沒有 generics。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.