首页 > 解决方案 > 函数参数的通用类型

问题描述

我试图理解一些对我来说看起来很明显的东西,但是 Typescript 似乎不允许使用所有严格的标志(当然有很好的理由)。让我告诉你:

我们都知道:

export interface Basic {
  value: "foo" | "bar" | "baz";
}

export interface Constraint extends Basic {
  value: "foo";
}

作品。但更令人惊讶的是(至少对我而言):

export interface WithFunctionsBasic {
  value: (t: "foo" | "bar" | "baz") => void;
}

export interface WithFunctionsConstraint extends WithFunctionsBasic { // WithFunctionsConstraint extends incorrectly WithFunctionsBasic
  value: (t: "foo") => void;
}

没有。

我们可以让它通用以允许我们这样的行为:

export interface WithFunctionsParametric<T extends Basic> {
  value: (t: T["value"]) => void;
}

const variableConstraint: WithFunctionsParametric<Constraint> = { value: (t: "foo") => {} };
const variableBasic: WithFunctionsParametric<Basic> = { value: (t: "foo" | "bar" | "baz") => {} };

两者都有效。但这有点棘手,因为尽管 Constraint 扩展了 Basic:

const variableConstraint: WithFunctionsParametric<Constraint> = { value: (t: "foo") => {} };
const variableBasic: WithFunctionsParametric<Basic> = variableConstraint; // Not Working

据我了解,如果 Constraint 扩展 Basic(就是这种情况),后者应该可以工作。有人可以解释一下为什么吗?

标签: typescripttypescript-generics

解决方案


您的问题从根本上源于输入和输出在继承方面的行为方式之间的重要区别。

让我们看第一个例子:

export interface Basic {
  value: "foo" | "bar" | "baz";
}

export interface Constraint extends Basic {
  value: "foo";
}

在这里,Constraint扩展Basic。两者都有属性value,但他们不同意它的类型。

  • Basic说它必须是"foo","bar""baz". 很公平。
  • Constraint说它只能是"foo"。这是兼容的,因为它是Basic' 的一组有效值的子集。

换句话说,一个 的实例Constraint不会破坏Basic做出的承诺。value仍然始终是"foo""bar"之一"baz"Constraint只会输出一个的事实是无关紧要的;它仍然承诺返回值来自这三个值的集合。

现在,让我们看第二个例子:

export interface WithFunctionsBasic {
  value: (t: "foo" | "bar" | "baz") => void;
}

export interface WithFunctionsConstraint extends WithFunctionsBasic { // WithFunctionsConstraint extends incorrectly WithFunctionsBasic
  value: (t: "foo") => void;
}

现在,这是什么意思?好吧,WithFunctionsBasic做出一个承诺:“我有一个名为的函数value,它接受 a "foo"、 a"bar"或 a "baz"”。

现在,WithFunctionsConstraintextends WithFunctionsBasic,这意味着它必须保持与做出的相同的承诺WithFunctionsBasic。嗯,是吗?

不,不是的。它的value功能只接受一个"foo". 换句话说,我们正在尝试创建一个WithFunctionsBasic子类型来打破 a 的含义WithFunctionsBasic,因为它的value函数不能接受"bar"or "baz"

当涉及到输出时,子类型必须具有相同或更强的限制。进一步限制输出的类型不会破坏超类型的承诺。在形式上,返回类型是协变的;他们被允许通过继承变得更加具体。

另一方面,当涉及到输入时,子类型必须具有相同或较弱的限制。也就是说,它必须至少接受相同的输入(以遵守承诺),但也可能允许超类型不允许的其他类型。在形式上,函数参数是逆变的;他们被允许通过继承变得不那么具体。

WithFunctionsConstraint接受"foo"是无效的,因为这意味着 aWithFunctionsConstraint不是有效的WithFunctionsBasic。但是,让它接受"foo""bar""baz"和是完全有效的"lol""wut"

您在问题中面临的其他问题似乎都是同一基本问题的不同变体。


推荐阅读