[英]Typescript recursive type with indexer
我想定義一個這樣的遞歸類型,基本上是這樣的:
interface CSSProperties {
marginLeft?: string | number
[key: string]?: CSSProperties
}
不幸的是, 打字稿文檔說:
盡管字符串索引簽名是描述“字典”模式的強大方法,但它們也強制所有屬性與其返回類型匹配。 這是因為字符串索引聲明obj.property也可以作為obj [“ property”]使用。 在以下示例中,名稱的類型與字符串索引的類型不匹配,並且類型檢查器給出錯誤:
這似乎無法用打字稿來表達,這似乎是一個嚴重的局限。 Flow在這里做了我認為正確的事情,並假設marginLeft
不屬於索引規范。
在TypeScript中根本可以做到嗎? 或者,是否可以指定一個字符串是除一組字符串之外的任何字符串? 這樣,我可以做大概的事情:
interface NestedCSSProperties: CSSProperties {
[P not in keyof CSSProperties]?: CSSProperties
}
這里的問題不是真正的遞歸(允許),而是您指出的簽名沖突。
我不確定當前行為的反面是否正確。 對於我來說,這似乎是一種主觀決定,可以同時為兩種方式做出案例。 照原樣接受示例意味着您將隱式合並定義。 您可以查看其中的一行,並假定界面的效果,而另一行更改結果。 編寫起來確實感覺更自然,但是我不確定它是否像您通常不會期望的那樣安全。
而不管。 TypeScript確實具有與您期望的行為類似的行為,但是必須明確指出,字符串鍵也可以是string
或number
類型。 這將起作用:
interface CSSProperties {
marginLeft?: string | number,
[key: string]: CSSProperties|string|number,
}
例如,對於上面的接口,這是有效的:
let a: CSSProperties = {
marginLeft: 10,
name: {
marginLeft: 20,
}
};
這不是:
let a: CSSProperties = {
marginLeft: 10,
something: false, // Type 'boolean' is not assignable to type 'string | number | CSSProperties'.
something: new RegExp(/a/g), // Type 'RegExp' is not assignable to type 'CSSProperties'.
name: {
marginLeft: 20,
},
car: ["blue"], // Type 'string[]' is not assignable to type 'CSSProperties'.
};
它會正確地知道命名成員:
let name1: string | number = a.marginLeft; // OK, return type is string | number
a.marginLeft = false; // Blocked, Type 'false' is not assignable to type 'string | number'.
a["whatever"] = false; // Blocked, Type 'false' is not assignable to type 'string | number | CSSProperties'.
a["marginLeft"] = false; // Blocked, Type 'false' is not assignable to type 'string | number'.
但是,這里的問題是,您需要在讀取時CSSProperties
其他動態成員-它不知道這是CSSProperties
。
這不會被阻止:
a["whatever"] = 100;
它會抱怨:
let name3: CSSProperties = a["name"]; // Type is CSSProperties | string | number
但是,如果您明確鍵入,這將起作用:
let name3: CSSProperties = a["name"] as CSSProperties;
在這種情況下,您必須包含字符串和數字。 然后,您可以定義替代。 使用此示例,如果您嘗試將地圖分配給'marginLeft',則TS會投訴,但會允許其他所有事情。
任何其他限制,您可能需要編寫更深入的.d.ts文件,該文件可能使用映射的類型。
您可以使用諸如Pick的預設映射類型來簡化專有屬性,但我承認,有時候我很難將它們包裹在它們周圍。
interface IMap<T> {
[key: string]: T
}
type CSSPropertyValue = string | number | CSSPropertiesBase;
interface CSSPropertiesBase extends IMap<CSSPropertyValue>
{
}
interface CSSProperties extends CSSPropertiesBase
{
marginLeft?: string|number;
}
您是否嘗試過使用像這樣的路口類型?
interface CssPropertyValues {
marginLeft? :string |number;
}
interface RecursiveCssProperties {
[key: string]: CssProperties;
}
type CssProperties = CssPropertyValues & RecursiveCssProperties;
let foo: CssProperties = {};
let myMargin = foo.bar.really.foobar.marginLeft; //should work... myMargin is typed as string|number|undefined
交集類型是接口繼承的一種經常被忽略且功能更強大的替代方法
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.