繁体   English   中英

如何为 TypeScript 中的泛型类型编写泛型辅助函数?

[英]How to write generic helper functions for a generic type in TypeScript?

我无法理解 TypeScript generics 与默认值的相互作用,以及如何尽可能广泛地限制实用程序功能。 (这个例子是人为的,但它可以很容易地用少量的代码来显示问题。)

在建模一个简单的案例时,你有一个给定“值”的“编辑器”,你可以这样表达:

type Editor<V extends object = object> = {
    value: V
    set: (value: V) => void
}

编辑器是通用的,因此您可以进一步细化值的类型。 而且我已将默认值添加到通用参数,以便您可以更轻松地在不需要使其更具限制性的情况下省略它。

然后你有一个外部助手 function 被写成通用的,可以与任何编辑器一起使用(简化):

const get = <E extends Editor>(editor: E): E['value'] => {
    return editor.value
}

但是在尝试为这些编辑器编写工厂时,我遇到了 TypeScript 错误:

const create = <V extends object = object>(value: V): Editor<V> => {
    const editor: Editor<V> = {
        value,
        set: (value: V) => {
            const prev = get(editor)
            // ...
        }
    }

    return editor
}

错误是:

Argument of type 'Editor<V>' is not assignable to parameter of type 'Editor<object>'.
  Types of property 'set' are incompatible.
    Type '(value: V) => void' is not assignable to type '(value: object) => void'.
      Types of parameters 'value' and 'value' are incompatible.
        Type 'object' is not assignable to type 'V'.
          'object' is assignable to the constraint of type 'V', but 'V' could be instantiated with a different subtype of constraint 'object'.(2345)

为什么会这样? 这令人困惑,因为get似乎尽可能宽松,所以我认为它会涵盖所有子类型,但 TypeScript 仍然抱怨类型可能不匹配。

这是TypeScript 游乐场链接

原始游乐场的问题在这个游乐场中得到了解决,但我不知道我是否删除了您正在探索的功能。

这段代码可以编译,我认为这将是做事的基础......

type Editor<V extends object> = {
  value: V;
  set: (value: V) => void;
};

const get = <V extends object>(editor: Editor<V>): V => {
  return editor.value;
};

const create = <V extends object>(value: V): Editor<V> => {
  const editor: Editor<V> = {
    value,
    set: (value: V) => {
      const prev = get(editor);
      // ...
    },
  };

  return editor;
};

为了说明问题,您可以通过在操场上进行这些逐步更改来重新创建我的工作示例中的原始错误。

首先将get的签名更改为此,它引入了一个潜在的缩小类型 E...

const get = <V extends object, E extends Editor<V>>(editor: E): V => {
  return editor.value;
};

然后通过确保缩小类型实际上正是编辑器实例的类型来修复它......

const prev = get<V,typeof editor>(editor);

...这澄清了与我没有它的工作代码相比,有一个额外的松散类型需要确定。

关于 Generics 和 Defaults 之间相互作用的未解决问题,因此您不是唯一一个。 我有一些启发和学习...

A)如果类型是参数组合的一部分,请始终在包含该参数的 function 定义中包含该类型,消除任何地方的默认值,并且永远不要默认为any

B)在实践中,我发现 Typescript 几乎总是从参数中推断出类型,但前提是它们已通过遵循 A)保留,以便它们存在于 Typescript 的代码路径中进行检查。

这可能解释了为什么我对您的代码的第一次干预是删除默认值,第二次是确保对 Editor 的任何引用始终伴随着可以从中推断出的 Generic V。

哦,C)如果一个类型不需要别名(并且可能缩小),请不要添加缩小的“别名”,因为您可能会创建一个稍后发生冲突的别名。

因此,例如,如果 V 是能够定义传递编辑器的 function 签名所需的全部内容,那么不要创建别名E extends Editor<V> ,或者更糟糕的是E extends Editor ,这样以后的签名可能会意外地引入相当于别名E2 extends Editor<V2>可能是也可能不是同一件事。

暂无
暂无

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

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