繁体   English   中英

有没有办法用接口内的另一种类型替换泛型类型?

[英]Is there a way to replace a generic type by another type inside an interface?

我有一个包含这些接口的文件:

export type Flatten<T> = T extends any[] ? T[number] : T;

export interface IGeneric<T> {
  data: T extends any[] ? T : Flatten<T> | null;
  meta: IGenericMeta;
}

export type IGenericData<T> = Pick<IGeneric<T>, 'data'>;

export interface IGenericMeta {
  pagination?: {
    page: number;
    pageSize: number;
    pageCount: number;
    total: number;
  };
}

// I use it like that :

interface ITest {
  id: number;
  attributes: {
    field1: string;
    relation1: IGenericData<ITest2>;
  }
}

interface ITest2 {
  id: number;
  attributes: {
    field1: string;
  }
}

当我检索数据时一切都很好,但是对于插入我不能再使用它了,我需要将字段relation1设置为number | null number | null

如果我这样设置关系:


// ...
interface ITest {
  id: number;
  attributes: {
    field1: string;
    relation1: IGeneric<ITest2> | number | null;
  }
}

const test: IGeneric<ITest> = {
  meta: {},
  data: {
    id: 1,
    attributes: {
      field1: '',
      relation1: {
        data: {
          id: 2,
          attributes: {
            field1: 'test',
          },
        },
      },
    },
  },
};
                                      ↓ error here
if (test.data?.attributes?.relation1?.data?.id) {
}

Typescript 对我大喊:

Property 'data' does not exist on type 'number | IGenericData<ITest2>'.
  Property 'data' does not exist on type 'number'.ts(2339)

我可以设置两个接口来扩展基础接口,如下所示:

interface ITestBase {
  id: number;
  attributes: {
    field1: string;
  }
}

type ITestInsert = ITestBase & {
  attributes: {
    relation1: number | null;
  }
}

interface ITestRetrieve = ITestBase & {
  attributes: {
    relation1: IGeneric<ITest2>;
  }
}

但我不太喜欢这种设置,因为我已经有很多接口,而且我不打算花整整一周的时间来重构这种方式。

我想找到一种使用泛型类型(也许?)的方法来覆盖我之前设置的行为,以将所有 IGenericData 替换为number | null number | null

type Replace<T> = ???

type Test = Replace<ITest['attributes']>;

也许这个问题已经得到回答,但我没有找到任何与我想做的事情相关的事情。 如果已经有答案,请提前道歉。

编辑1:

操场 typescript 更新

警告:这种深度类型转换通常有很多边缘情况,因此您应该针对您的用例彻底测试任何建议的解决方案,并准备根据结果修改或放弃它。 我将根据您问题中的示例进行回答。


看起来您希望Replace<T>成为递归条件类型 如果T是一些GenericData ,那么您将用number | null替换它 number | null 如果T是原始类型,您希望不理会它。 否则,如果T是某个 object 类型,您希望向下递归到它和map每个属性到该属性的Replace d 版本。

该方法转化为以下代码:

type Replace<T> = T extends IGenericData<any> ? (number | null) :
  T extends object ? { [K in keyof T]: Replace<T[K]> } : T;

让我们看看它是否有效:

type ReplacedITestAttributes = Replace<ITestAttributes>;
/* type ReplacedITestAttributes = {
    field1: string;
    relation1: number | null;
} */

type ReplacedITest = Replace<ITest>;
/* type ReplacedITest = {
    id: number;
    attributes: {
        field1: string;
        relation1: number | null;
    };
} */

看起来不错

Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM