如何删除重复类型并避免以 never 结尾?
How can you remove duplicate types and avoid ending up with never?
背景
我有一个接受 2 个类型参数的接口。第二个参数是可选的,默认为 void。我还有一个实用程序类型 (CommandReturnType
),如果未提供第二个类型参数,它使用条件类型来防止 void
成为 return 类型。
interface Command<IOType, AdditionalOutputType = void> {
execute(input: IOType): CommandReturnType<IOType, AdditionalOutputType>;
}
type CommandReturnType<A, B> = B extends void ? A : A | B;
我创建了一个实用程序类型,它使用 infer
到 return 一个包含 2 种类型参数类型的元组。
type GetCommandParamTypes<T extends Command<unknown, unknown>> =
T extends Command<infer IO, infer A> ?
[IO, A] :
never;
如果我为它提供只有一个类型参数的接口,它会按预期工作 - 它正确地 returns void
作为第二个参数。
type type1 = GetCommandParamTypes<Command<string>>; // [string, void]
但是,如果我为它提供一个 class 来实现仅具有一个类型参数的接口,那么它 return 是一个在两个槽中都具有该类型的元组。
class TypeACommand implements Command<string> {
execute(input: string): string {
return input;
}
}
type type2 = GetCommandParamTypes<TypeACommand>; // [string, string]
尝试的解决方案
与我打算 post 就 SO 提出的大多数问题一样,当我写出问题时,它有助于澄清实际发生的事情。
我(认为我)现在明白在检查 class 时检索 execute
函数的输入类型和 return 类型。所以我想到我可以使用 Exclude<>
从第二种类型中删除任何重复项。
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<type type>
returns 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]
我在这个
中找到了解决方案
Conditional types distribute over naked type parameters. This means that the conditional type gets applied to each member of the union. never is seen as the empty union. So the conditional type never gets applied (since there are no members in the union to apply it to) resulting in the never type.
The simple solution is to disable the distributive behavior of conditional types using a tuple:
如此简单地改变
type ReplaceNever<T> = T extends never ? void : T;
到
type ReplaceNever<T> = [T] extends [never] ? void : T;
解决了我的问题。
背景
我有一个接受 2 个类型参数的接口。第二个参数是可选的,默认为 void。我还有一个实用程序类型 (CommandReturnType
),如果未提供第二个类型参数,它使用条件类型来防止 void
成为 return 类型。
interface Command<IOType, AdditionalOutputType = void> {
execute(input: IOType): CommandReturnType<IOType, AdditionalOutputType>;
}
type CommandReturnType<A, B> = B extends void ? A : A | B;
我创建了一个实用程序类型,它使用 infer
到 return 一个包含 2 种类型参数类型的元组。
type GetCommandParamTypes<T extends Command<unknown, unknown>> =
T extends Command<infer IO, infer A> ?
[IO, A] :
never;
如果我为它提供只有一个类型参数的接口,它会按预期工作 - 它正确地 returns void
作为第二个参数。
type type1 = GetCommandParamTypes<Command<string>>; // [string, void]
但是,如果我为它提供一个 class 来实现仅具有一个类型参数的接口,那么它 return 是一个在两个槽中都具有该类型的元组。
class TypeACommand implements Command<string> {
execute(input: string): string {
return input;
}
}
type type2 = GetCommandParamTypes<TypeACommand>; // [string, string]
尝试的解决方案
与我打算 post 就 SO 提出的大多数问题一样,当我写出问题时,它有助于澄清实际发生的事情。
我(认为我)现在明白在检查 class 时检索 execute
函数的输入类型和 return 类型。所以我想到我可以使用 Exclude<>
从第二种类型中删除任何重复项。
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<type type>
returns 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]
我在这个
Conditional types distribute over naked type parameters. This means that the conditional type gets applied to each member of the union. never is seen as the empty union. So the conditional type never gets applied (since there are no members in the union to apply it to) resulting in the never type.
The simple solution is to disable the distributive behavior of conditional types using a tuple:
如此简单地改变
type ReplaceNever<T> = T extends never ? void : T;
到
type ReplaceNever<T> = [T] extends [never] ? void : T;
解决了我的问题。