簡體   English   中英

帶索引器的Typescript遞歸類型

[英]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確實具有與您期望的行為類似的行為,但是必須明確指出,字符串鍵也可以是stringnumber類型。 這將起作用:

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.

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