繁体   English   中英

在 Typescript 中删除对象键并返回条件类型

[英]Delete object keys and return conditional type in Typescript

我有两个接口来描述不同的消息

interface MessageA {
  timestamp: number
}

interface MessageB {
  name: string
}

和一个接口来描述他们的共同领域

interface CommonMessage {
  text: string
  url: string
}

我通过添加使用const enum声明的类型的额外字段将它们组合成一个新类型

const enum Type {
  A,
  B,
}

type CombinedMessage =
  | (MessageA & CommonMessage & { type: Type.A })
  | (MessageB & CommonMessage & { type: Type.B })

此外,我定义了一个类型,它删除了CombinedMessage中的公共字段

type DistributiveOmit<T, K extends string & keyof T> = T extends any ? Omit<T, K> : never
type Message = DistributiveOmit<CombinedMessage, keyof CommonMessage | 'type'>

现在,我想定义一个函数来将CombinedMessage转换为Message

const omit = <T, K extends string & keyof T>(value: T, keys: readonly K[]): DistributiveOmit<T, K> => {
  const ret = { ...value }
  for (const key of keys) {
    delete ret[key]
  }
  return ret
}

定义omit函数后,我可以定义

function removeCommonFields(combinedMessage: CombinedMessage): Message {
  return omit(combinedMessage, ['text', 'url', 'type'])
}

得到我想要的。

但是, Typescript编译器显示该行

return ret

在函数中omit有错误

Type 'T' is not assignable to type 'DistributiveOmit<T, K>'.ts(2322)

它表明这里的ret是泛型类型T ,所以我不能作为DistributiveOmit<T, K>类型返回。

似乎delete操作对ret类型没有任何作用。

目前,我使用return ret as unknown as DistributiveOmit<T, K>来避免编译器显示错误; 但是,我想尽可能避免使用类型断言。

还有其他想法吗? 谢谢。

这或多或少是 TypeScript 的设计限制。 在这里您无法真正避免类型断言

编译器推迟对依赖于尚未指定的泛型类型参数的条件类型的评估,因此它无法真正验证任何东西都可以分配给这些类型。

Omit<X, K>被视为X的超类型,因此Omit<X, K> | Omit<Y, K> | Omit<Z, K> Omit<X, K> | Omit<Y, K> | Omit<Z, K> Omit<X, K> | Omit<Y, K> | Omit<Z, K>将被视为X | Y | Z的超类型X | Y | Z X | Y | Z ,编译器不知道如何将该分布推广到DistributiveOmit<T, K>omit()的主体内,其中T未指定。

microsoft/TypeScript#33912 上有一个开放功能请求,要求为返回条件类型的泛型函数提供更多编译器支持。 看起来在不久的将来没有任何明显的解决方案,即使有它在您的特定示例中也可能不起作用。 因此,就目前而言,您能做的最好的事情是类型断言(或类似单个调用签名重载的等效内容)。


我能想到的唯一轻微改进是使用Omit<T, K>而不是unknown作为您的中间断言类型:

return ret as Omit<T, K> as DistributiveOmit<T, K>; // no error

这将有助于捕获您意外返回ret以外的其他内容的错误:

// return "oopsie" as unknown as DistributiveOmit<T, K>; // no error
// return "oopsie" as Omit<T, K> as DistributiveOmit<T, K>; // error!

这可能总比没有好,但也好不了多少。

Playground 链接到代码

暂无
暂无

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

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