网站搜索

typescript - 条件类型扩展字符串字面量


我想添加一个类型工具,它应该有条件地添加 T。

查看 Codesandbox

这个想法如下:

类似这样:type IfType<If, Eq, T>

  • If 是一种 字符串字面量,例如 "Cat" | "Dog"。
  • Eq 是 If 提取的字符串字面量,例如 "Cat" 或 "Dog"
  • T 是我想返回的类型,如果传入的 泛型类型 是那个特定类型的话。

T 扩展了一个 object,但如果 Eq 不是 equal,那么它会返回 Partial<T>,以便类型仍然知道 object 的键(用于解构)。

像这样的东西:

util.ts

type IfType<
  If extends string,
  Eq extends If,
  T extends object
> = If extends Extract<If, Eq> ? T : Partial<T>

otherfile.ts

type Type = 'read' | 'write';

type Props<T extends Type> = { 
  type: T 
} & (
IfType<
  T,
  // This gets the ts(2344) error, and I don't know how to stop it
  "write",
  { descriptionOnlyForWrite: string }
> 
| IfType<
  T,
  // This gets the ts(2344) error, and I don't know how to stop it
  "read",
  { hasRead: boolean}
>);

这会产生这个错误:

Type '"write"' does not satisfy the constraint 'T'.
  '"write"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Type'.

关于如何输入辅助工具类型有什么建议吗?

额外信息:我通常会在没有 utils 类型的情况下这样输入,这样可以按我想要的方式工作

type Type = 'read' | 'write';

type Props<T extends Type> = {
  type: T;
} & (
  | (T extends 'write' ? { descriptionOnlyForWrite: string } : { descriptionOnlyForWrite?: undefined })
  | (T extends 'read' ? { hasRead: boolean } : { hasRead?: undefined })
);

答案#1

事实是 T extends Type 和 "write" extends Type 并不意味着 "write" extends T。考虑当 T 为 "read"(例如 Props<"read">)时会发生什么;"write" extends "read" 并不成立,因此编译器会报错,提示 IfType<T, "write", XXX> 可能无效。

看起来你并不真的希望 IfType<If, Eq, T> 要求 Eq extends If。假设你不想让 IfType 变得更复杂,你可以直接去掉 Eq 上的约束:

type IfType<
    If extends string,
    Eq, // <-- no constraint here
    T extends object
    > = If extends Extract<If, Eq> ? T : Partial<T>

然后以下内容按预期编译:

type Props<T extends Type> = { type: T } & (
    IfType<T, "write", { descriptionOnlyForWrite: string }>
    | IfType<T, "read", { hasRead: boolean }>
);

如果你真的关心在 If 和 Eq 上表达约束,你将需要将该约束作为单独的类型参数 C 传递给 IfType:

type IfType<
    C extends string,
    If extends C,
    Eq extends C,
    T extends object
    > = If extends Extract<If, Eq> ? T : Partial<T>

type Props<T extends Type> = { type: T } & (
    IfType<Type, T, "write", { descriptionOnlyForWrite: string }>
    | IfType<Type, T, "read", { hasRead: boolean }>
);

但我怀疑这对你的使用场景真的有必要。毕竟,IfType 实际上从未使用 C 来计算输出类型;它只是对 If 和 Eq 进行的一种合理性检查。除非你有特别重要的理由这样做,否则我会直接完全移除对 Eq 的约束。

代码的 Playground 链接