【问题标题】:How can you remove duplicate types and avoid ending up with never?如何删除重复类型并避免以 never 结束?
【发布时间】:2021-12-16 22:32:55
【问题描述】:

背景

我有一个接受 2 个类型参数的接口。第二个参数是可选的,默认为 void。我还有一个实用程序类型 (CommandReturnType),它使用条件类型来防止 void 在未提供第二个类型参数时成为返回类型。

interface Command<IOType, AdditionalOutputType = void> {
    execute(input: IOType): CommandReturnType<IOType, AdditionalOutputType>;
}

type CommandReturnType<A, B> = B extends void ? A : A | B;

我创建了一个实用程序类型,它使用infer 返回一个包含 2 种类型参数类型的元组。

type GetCommandParamTypes<T extends Command<unknown, unknown>> =
    T extends Command<infer IO, infer A> ?
    [IO, A] :
    never;

如果我为它提供只有一个类型参数的接口,它会按预期工作 - 它正确返回 void 作为第二个参数。

type type1 = GetCommandParamTypes<Command<string>>; // [string, void]

但是,如果我为它提供一个实现接口的类,该接口只有一个类型参数,它会在两个槽中返回一个具有该类型的元组。

class TypeACommand implements Command<string> {

    execute(input: string): string {
        return input;
    }
}

type type2 = GetCommandParamTypes<TypeACommand>; // [string, string]

尝试的解决方案

与我打算在 SO 上发布的大多数问题一样,当我写出问题时,它有助于澄清实际发生的情况。

我(我想我)现在明白execute 函数的输入类型和返回类型是在检查类时检索的。所以我突然想到我可以使用Exclude&lt;&gt; 从第二种类型中删除任何重复项。

 type GetCommandParamTypes<T extends Command<unknown, unknown>> =
    T extends Command<infer IO, infer A> ?
    [IO, Exclude<A, IO>] :
    never;

当为第二个类型参数提供类型时效果很好:

class MixedTypeCommand implements Command<string | number, Function> {

    execute(input: string | number): string | number | Function {
       // do something with input here...
       return something;
    }
}

type type3 = GetCommandParamTypes<MixedTypeCommand>; // [string | number, Function] instead of [string | number, string | number | Function]

但是Exclude&lt;type type&gt; 在类型参数匹配时返回never

type type4 = GetCommandParamTypes<TypeACommand>; // [string, never]

所以我想出了一个不那么狡猾的计划,将never 替换为void。失败了。

type ReplaceNever<T> = T extends never ? void : T;

type GetCommandParamTypes<T extends Command<unknown, unknown>> = 
    T extends Command<infer IO, infer A> ?
    [IO, ReplaceNever<Exclude<A, IO>>] :
    never;

type type5 = GetCommandParamTypes<TypeACommand>; // [string, never]

问题

有没有简单的方法可以替换never

如果没有,是否有其他解决方案可以让我从第二个元组插槽中删除重复类型,如果结果没有,请将其替换为 void

所以:

 type wrong = GetCommandParamTypes<TypeACommand>; // [string, string]

会变成:

 type right = GetCommandParamTypes<TypeACommand>; // [string, void]

【问题讨论】:

  • 请记住,这里GetCommandParamTypes&lt;TypeACommand&gt; 是从类实例推断的。这意味着 TS 能够推断方法的返回类型。这就是为什么你得到[string, string] 而不是[stirng, void]
  • @captain-yossarian 感谢您的回复。是的,我提到我理解这是传递类类型时发生的情况。你的意思是类类型而不是实例?
  • TypeACommand 是一种类实例。这很棘手,因为typeof ypeACommand 是不同的类型

标签: typescript generics typescript-generics


【解决方案1】:

我在SO answer找到了解决方案

条件类型分布在裸类型参数上。这意味着条件类型将应用于联合的每个成员。从不被视为空的联合。所以条件类型永远不会被应用(因为联合中没有成员可以应用它)导致永远类型。

简单的解决方案是使用元组禁用条件类型的分配行为:

简单的改变

type ReplaceNever<T> = T extends never ? void : T;

type ReplaceNever<T> = [T] extends [never] ? void : T;

解决了我的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2014-12-04
    • 2012-01-07
    • 2020-09-17
    相关资源
    最近更新 更多