首页 > 解决方案 > 如何区分 TypeScript 中结构相同的类型?

问题描述

我正在使用一个库,它有几个结构上等效的接口。像这样:

interface DataType { ... some stuff here ... }
interface DataTypeBoolean extends DataType {}
interface DataTypeJSON extends DataType {}
... around 20 more, not all of them equivalent

它给了我一个类型为:

def: {
    flag: {
        type: DataTypeBoolean;
    };
    data: {
        type: DataTypeJSON;
    };
}

我想生成一个将这些转换为原始类型的类型:

export type Types<T> = { [K in keyof T]:
  T[K] extends { type: DataTypeBoolean } ? boolean :
  T[K] extends { type: DataTypeJSON } ? object :
  ... some more clauses
  undefined;
}

但是因为DataTypeBooleanandDataTypeJson是等价的,所以DataTypeJSON产生and 因此,给了我:trueextends DataTypeBooleanTypes<def>

def: {
    flag: boolean;
    data: boolean;
}

为此Types<def>,我必须找到某种方法来创建DataTypeBooleanStrict和(DataTypeJsonStrict这样我仍然可以尊重库接口),但也不能扩展,反之亦然。DataTypeBooleanStrict extends DataTypeBooleanDataTypeJSONStrict extends DataTypeJSONDataTypeBooleanStrictDataTypeJSONStrict

我怎样才能做到这一点?

使用泛型类型来实现这一点会非常好,Strict<DataTypeBoolean>而不是必须手动定义所有 30 个,但手动也比没有好。

标签: typescript

解决方案


如前所述,TypeScript 中的类型系统是结构化的,因此具有相同形状的两个类型实际上是相同的类型,即使它们可能有不同的名称。处理这个问题的方法通常是修改你的类型,使它们在结构上和名义上都不同,如 TypeScript FAQ 条目中所述:“如何防止两种类型在结构上兼容?”

现在在这种情况下,有问题的界面显然位于您不能或不想触摸的库中。幸运的是,您可以使用声明合并来重新打开接口定义并向它们添加新属性。可能您想添加一些可选的东西,以便在运行时不存在实际的此类属性是可以的。例如:

///// DECLARATION MERGING //////
interface DataTypeBoolean {
    __type?: "boolean"
}
interface DataTypeJSON {
    __type?: "JSON"
}
///////////////////////////////

请记住,如果您从模块中导入接口,您可能需要使用declare module合并定义,如手册中所示

declare module "./myLib" {
  interface DataTypeBoolean {
      __type?: "boolean"
  }
  interface DataTypeJSON {
      __type?: "JSON"
  }
}

无论如何,一旦我们这样做了,事情应该开始为你工作,因为编译器可以看到这些类型是不同的:

type Hmm = Types<Def>;
/* type Hmm = {
    flag: boolean;
    data: object;
} */

好的,希望有帮助。祝你好运!

链接到代码


推荐阅读