使用Typescript为拥有动态和静态键的对象创建接口

12

我正在尝试学习TypeScript,但是当涉及到接口时遇到了障碍。我有一个对象,我想要将一个token和一个route保存在其中,如下所示:

const obj = {
  token: 'thisismytoken',
  '/path/to/somewhere': [{ ... }]
}

我在这里遇到的问题是:如何为这个对象生成接口?
我尝试过:
interface iObject {
  token: string;
  [key: string]: { ... }[]
}

但是这会生成错误:

TS2411: 类型为 'string' 的属性 'token' 无法分配给类型 '{...}' 的字符串索引类型。

当我尝试类似以下代码时,同样的问题也会出现:

interface iRoute {
  val1: number;
  val2: string;
}

interface iObject extends iRoute {
  token: string;
}

当我尝试下列操作时:
interface iObject {
  [key: string]: { ... }[] | string;
}

当我尝试向路由变量添加数据时,出现以下错误:

TS2339:类型“string | { ... }[]”上不存在属性“push”。
类型“string”上不存在属性“push”。

是否有其他方法可以解决这个问题?

我认为这应该是一种语言特性,因此我在GitHub上开了一个问题 https://github.com/microsoft/TypeScript/issues/43057 - Danielo515
2个回答

5
您可以使用交叉类型来实现这个功能:
type iObject = {
    token: string
} & {
    [key: string]: { ... }[]
}

但请注意,索引类型并不限制您只能使用一个属性。您可能需要的是经过正则表达式验证的键,这将在未来可能推出


在分配类型时无法工作:https://femto.pw/hej8 - Jorge Fuentes González
3
这是不正确的。请不要提出根本行不通的解决方案。 - Danielo515
我不确定有什么抱怨,这对我很有效。混合静态和动态键是不美观的,但我被困在别人的模式中。在我的情况下,动态键具有一定的模式,并且由于我使用的是TS 4.1+,我能够更加具体:type Meeting = {[name: `Sensor_${number}`]: SensorMeetingRecord} & {id: number; topic: string; startTime: number; ... } - Robert Rendell
1
在@RobertRendell的情况下,键不会重叠。但是,不,上面的方法不适用,因为[key: string]会重叠token字段,并且还需要匹配。话虽如此,我有一些非常复杂的情况,使用了接口和类,而且效果很好...真的不知道为什么。也许这是个bug? - undefined

4
你可以尝试像这样做。你正在尝试创建一个字典,这可能会有所帮助。
为了拥有干净的代码, 第一种方法
interface IDictionary<T> {
    [key: string]: T;
}

interface iObject {
    token: string;
    route: IDictionary<any[]>;
}

//Use it like this
var obj: iObject = {
            token: "thisismytoken",
            route: {
                "/path/to/somewhere": [{
                    pathName: 'somewhere'
                }]
            }
        };

这里有一个关于IT技术的typescript示例代码,包含了一个名为"createToken"的类和一些接口以及对象。你可以在typescript playground上测试这段代码。

第二种方法 基于您上次尝试的内容

当我尝试以下内容时:

interface iObject { [key: string]: { ... }[] | string; }

    interface IDictionary<T> {
        [key: string]: T;
    }

    var obj: IDictionary<any> = {
            token: "asdf123",
            "path/to/somewhere": [{
                data: 'something'
            }]
        };

TS2339: 属性'push'在类型'string | {...}[]'上不存在。 在类型'string'上不存在属性'push'。

第三种方法

你可以这样将你的对象进行类型转换:

interface iObject {
  [key: string]: Array<object> | string;
}

self.obj = {
            token: self.tokenVal
        };
        self.obj[self.route] = [{ "routeName": self.routeName }];

        (<Array<object>>self.obj[self.route]).push({
            "zip": "123456"
        });

还可以参考高级类型


嗨,谢谢。不过我已经深入到数组中了,这让我感到有些不舒服。如果唯一的原因是为了让接口工作而添加额外的层级,我不想这样做。 - Touchpad
@Touchpad 你只能使用IDictionary接口,因为它是泛型的。你不需要做任何改变。数组也可以使用,因为类型可以是任意的。 - Hey24sheep
@Touchpad,我已经更新了我的答案,包括另一种方法。 - Hey24sheep
第一种方法会导致额外的层级(route)。至于第二种方法,如果可能的话,我真的想避免使用 any - Touchpad
@Touchpad 我明白你的意思了,请查看我的最新更新。希望能对你有所帮助。 - Hey24sheep

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接