【问题标题】:Cloning a type and using it's keys克隆类型并使用它的键
【发布时间】:2021-10-26 14:09:02
【问题描述】:

我知道这听起来像是科学怪人的怪物想法,但请听我说完。我有一个类型:

export enum detailsDataTypes {
  MACHINE = 'MACHINE',
  USER = 'USER',
  ABSTRACT = 'ABSTRACT',
}

export type sharedTypes = {
  name?: string;
  OEECategory?: string;
  OAECategory?: string;
  color?: string;
};

export type AbstractData = sharedTypes & {
  layer: string;
  type: detailsDataTypes.ABSTRACT;
};

export type UserData = sharedTypes & {
  defaultTimeout: number;
  stateAfterTimeout: string;
  accessCanChoose: string;
  accessCanOverwrite: string;
  type: detailsDataTypes.USER;
};

export type MachineData = Omit<UserData, 'type'> & {
  stateCategory: string;
  canOverwriteOnlyByStates: boolean;
  machineType: string;
  type: detailsDataTypes.MACHINE;
};

而且这种类型适合包含数据。但我想要另一种类型来保存标题(人类可读)。这是我的想法:

const sharedHeaders: sharedTypes = {
  name: 'Name',
  OEECategory: 'OEE category',
  OAECategory: 'OAE category',
  color: 'Color',
};

export const AbstractHeaders: Omit<AbstractData, 'type'> = { ...sharedHeaders, layer: 'Layer' };

export const UserHeaders: Omit<UserData, 'type'> | { defaultTimeout: string } = {
  ...sharedHeaders,
  defaultTimeout: 'Default timeout',
  stateAfterTimeout: 'State after timeout',
  accessCanChoose: 'Access - can choose',
  accessCanOverwrite: 'Access - can overwrite',
};

export const MachineHeaders: Omit<MachineData, 'type'> = {
  ...UserHeaders,
  stateCategory: 'State category',
  machineType: 'Machine type',
  canOverwriteOnlyByStates: 'Can overwrite only by states',
};

但是这里我们有一堆类型冲突,我不禁认为必须有更好的方法来做到这一点。

例如在这里,我得到解构后的所有内容都是字符串,它不能分配给从不输入。

export const MachineHeaders: Omit<MachineData, 'type'> = {
  ...UserHeaders,
  stateCategory: 'State category', //here
  machineType: 'Machine type', //here
  canOverwriteOnlyByStates: 'Can overwrite only by states', //here
};

有没有更好的方法来实现我的目标?还是我想错了?

【问题讨论】:

  • 我对@9​​87654325@ 应该是什么感到困惑。是否有意使用type MachineData = UserData &amp; ... 而不是type MachineData = sharedTypes &amp; ...?如果是这样,你能解释一下吗?如果不是,这整个问题只是一个错字的结果还是发生了其他事情?
  • 基本上UserDatasharedData + 附加属性,MachineDataUserData + 附加属性。
  • 嗯,但是您的“附加”属性之一是type,它与来自UserDatatype 冲突; type 不可能同时是两个不同的字符串文字,因此编译器将整个事情简化为 never... this 会为您工作吗?如果是这样,我会写一个答案;如果不是,请详细说明。
  • 是的,这几乎是完美的。我尝试将canOverwriteOnlyByStates 转换为字符串,就像您对defaultTimeout 所做的那样,但无法使其正常工作。你能详细说明吗? + 为什么这行得通,我的方法有什么问题?
  • 已编辑。现在应该可以了

标签: javascript reactjs typescript types


【解决方案1】:

当您有一个对象类型并希望以编程方式从中创建一个新的对象类型时,您通常可以使用a mapped type 来实现。


在您的情况下,您有一堆对象类型,它们的属性值类型是任意的,并且您想提出具有相同键但其属性都是 string 的新版本,用作标题值表或其他东西。

我们可以这样做:

type HeadersFor<T> = { [K in keyof T]: string };

让我们测试一下:

type X = HeadersFor<{ a: number, b: boolean, c?: Date, type: string }>;
/* type X = {
  a: string;
  b: string;
  c?: string | undefined;
  type: string;
} */

如您所见,每个属性都已转换为string。好吧,c 字段在输入类型中是 optional,在输出类型中仍然如此(因此可能包括 undefined)。如果您希望输出属性是必需的而不是可选的,您可以使用 mapping modifier 来实现:

type HeadersFor<T> = { [K in keyof T]-?: string };

type X = HeadersFor<{ a: number, b: boolean, c?: Date, type: string }>;
/* type X = {
  a: string;
  b: string;
  c: string
  type: string;
} */

太棒了!但是看看你的原件,你似乎也想完全省略 type 属性。如果需要,您可以组合 OmitHeadersFor,或者您可以重新定义 HeadersFor 以自动从映射类型中排除 type 键。有多种方法可以做到这一点,但从 TS4.1 开始,我可能会建议像这样使用key remapping via as

  type HeadersFor<T> = { [K in keyof T as Exclude<K, "type">]-?: string };

  type X = HeadersFor<{ a: number, b: boolean, c?: Date, type: string }>;
  /* type X = {
    a: string;
    b: string;
    c: string
  } */

这看起来就像你需要的一切,所以我们将保留 HeadersFor 的定义。


现在您可以毫无错误地执行此操作:

const sharedHeaders: HeadersFor<SharedTypes> = {
  name: 'Name',
  OEECategory: 'OEE category',
  OAECategory: 'OAE category',
  color: 'Color',
};

export const AbstractHeaders: HeadersFor<AbstractData> = { ...sharedHeaders, layer: 'Layer' };

export const UserHeaders: HeadersFor<UserData> = {
  ...sharedHeaders,
  defaultTimeout: 'Default timeout',
  stateAfterTimeout: 'State after timeout',
  accessCanChoose: 'Access - can choose',
  accessCanOverwrite: 'Access - can overwrite',
};

export const MachineHeaders: HeadersFor<MachineData> = {
  ...UserHeaders,
  stateCategory: 'State category',
  machineType: 'Machine type',
  canOverwriteOnlyByStates: 'Can overwrite only by states',
};

Playground link to code

【讨论】:

  • 好的,这很有效,非常棒,谢谢。你先生是一个TS巫师。你能解释一下{ [K in keyof T as Exclude&lt;K, "type"&gt;]-?: string };中的: string部分吗?这是我在文档中找不到的一件事。我假设这是将所有类型“转换”为字符串的原因,但语法是什么以及它是如何工作的?
  • mapped types 的文档中,它显示了{[K in XXX]: boolean} 如何导致所有键的输出属性值类型为boolean。希望在那里为您解释语法。
  • 是的,这是有道理的。谢谢!
猜你喜欢
  • 2018-03-26
  • 2014-01-09
  • 1970-01-01
  • 1970-01-01
  • 2017-02-18
  • 2011-01-19
  • 2015-06-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多