【问题标题】:Exclude `{}` from conditional mapped type从条件映射类型中排除“{}”
【发布时间】:2022-08-18 16:06:40
【问题描述】:

我们在以下界面上工作

interface A {
    a: string
    b: string
    c?: number
    d?: number
}

而且我们有一种类型可以使T 中的每个键可选的如果他们的类型是 string 并且必需的如果是number

type B<T> = {                                      
    [K in keyof T as T[K] extends (number|undefined) ? K : never]-?: T[K]                      
} & {
    [K in keyof T as T[K] extends (string|undefined) ? K : never]+?: T[K] 
}

/* The resulting type will be:
type B<A> = {
    c: number;
    d: number;
} & {
    a?: string | undefined;
    b?: string | undefined;
}
*/

但是,如果我们将正在处理的接口更改为仅包含条件中指定的类型之一,{},它几乎对应于any 将被添加到结果类型

interface A1 {
    a: string
    b: string
}

/* The resulting type will be:
type B<A1> = {} & {
    a?: string | undefined;
    b?: string | undefined;
}
*/

这将允许将许多不需要的类型分配给 B,从而破坏目的。例如

const b: B<A1> = \"We don\'t want this to happen.\" // <-- We need an error here.

问题

如何防止生成的类型包含 {} ?我希望B&lt;A1&gt; 产生以下类型

{
    a?: string | undefined;
    b?: string | undefined;
}

游乐场链接

我通过删除泛型简化了类型,因此结果类型是可见的。你可以查看here

  • GitHub 跟踪器中有一些问题涉及与空对象的交集,但我没有发现这个特定问题。一种解决方法,试试这个助手(具有讽刺意味的是,它涉及与一个空对象相交):type Expand&lt;T&gt; = {} &amp; { [P in keyof T]: T[P] };
  • 谢谢你。我自己很难找到这个修复程序。我应该就此提出问题还是预期的行为?我认为您可以使用此解决方法发布答案,也许可以解释其工作原理。
  • 我很惊讶这还没有被报道(社区非常积极地报道这种事情),但如果你没有找到它,你可以试试。顺便说一句,这也应该有效:type Expand2&lt;T&gt; = { [P in keyof T]: T[P] };。对我来说它看起来像一个错误,如果您粘贴类型 C 并使用它,那么它就可以工作,这对我来说毫无意义。
  • 实际上是works。如果您发布答案,我会接受。
  • @soffyo 我认为这是根本问题:github.com/microsoft/TypeScript/issues/42864。如果您创建type D = {[K in never]: any} &amp; {a?: string | undefined, b?: string | undefined},您会看到它也显示为{} &amp; {...},并表现出与C 相同的行为。

标签: typescript typescript4.0


【解决方案1】:

@Aplet123 指出https://github.com/microsoft/TypeScript/issues/42864 是问题的根源。一种解决方法(可能还有其他更简单的方法):强制它成为对象类型的助手:

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

const b: Obj<B<A1>> = "We don't want this to happen." // Error! Good.

【讨论】:

    【解决方案2】:

    你可以做这样的事情来解决你的问题。 通过映射对象类型 C,空对象消失,因为它没有任何键可以映射到。playground link

    export type Narrow<A> =
      | (A extends Narrowable ? A : never)
      | (A extends [] ? [] : never)
      | {
          [K in keyof A]: A[K] extends Function ? A[K] : Narrow<A[K]>;
        };
    
    
    type C =Narrow< {                                      
        [K in keyof A1 as A1[K] extends (number | undefined) ? K : never]-?: A1[K]                      
    } & {
        [K in keyof A1 as A1[K] extends (string | undefined) ? K : never]+?: A1[K] 
    }>
    /* Resulting type:
    type C = {
        a?: string | undefined;
        b?: string | undefined;
    }
     */
    
    const b: C = "This string should not be assignable to B!" // error
    

    【讨论】:

    猜你喜欢
    • 2019-03-21
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 2019-04-06
    • 2021-04-10
    • 1970-01-01
    • 2022-08-16
    相关资源
    最近更新 更多