带索引器的Typescript递归类型

时间:2017-04-11 23:18:36

标签: typescript recursion

我想像这样定义一个递归的类型,基本上是:

interface CSSProperties {
  marginLeft?: string | number
  [key: string]?: CSSProperties
}

不幸的是,typescript docs说:

  

虽然字符串索引签名是描述“字典”模式的有效方式,但它们还强制所有属性都与其返回类型匹配。这是因为字符串索引声明obj.property也可用作obj [“property”]。在以下示例中,name的类型与字符串索引的类型不匹配,type-checker给出错误:

这似乎说这不可能用打字稿来表达,这似乎是一个严重的限制。 Flow做了我认为正确的事情,并假设marginLeft不属于索引规范。

TypeScript中是否可以使用它?或者,有没有办法指定字符串是任何字符串一组字符串?这样,我可以大致做一些事情:

interface NestedCSSProperties: CSSProperties {
  [P not in keyof CSSProperties]?: CSSProperties
}

3 个答案:

答案 0 :(得分:3)

这里的问题并不是真正的递归(允许),而是指出了你所指出的冲突签名。

我不确定当前行为的反面是否正确。对于我来说这似乎是一个主观决定,可以通过两种方式进行。按原样接受示例意味着您隐式合并定义;您可以查看其中一行并假设界面的效果,而另一行更改结果。写作确实感觉更自然,但是我不确定它是否安全,因为你通常不会对类型定义感到沮丧。

不管。 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

这不会被阻止:

a["whatever"] = 100;

它会抱怨这个:

let name3: CSSProperties = a["name"]; // Type is CSSProperties | string | number

但如果明确地进行类型转换,这将有效:

let name3: CSSProperties = a["name"] as CSSProperties;

答案 1 :(得分:1)

在这种情况下,您必须包含字符串和数字。然后,您可以定义覆盖。使用这个例子,如果你试图将一个地图分配给'marginLeft',TS会抱怨,但会允许其他一切。

任何更多约束,您可能需要编写一个更深入的.d.ts文件,该文件可能使用映射类型。

您可以使用像Pick这样的预设映射类型来简化您的专属属性,但我承认,我有时很难将我的大脑包裹起来。

var picarioAPI = "where I pull the json file";

$.getJSON(picarioAPI, function (json) {
  for (i in json) 
    if (json[i].CustId == {{ customer.id }}) { 

答案 2 :(得分:1)

您是否尝试使用交叉点类型?

?- set_prolog_flag(answer_write_options,[max_depth(0)]).
true.

交叉点类型是界面继承的一种经常被忽略的,稍微强大的替代方法