打字稿:为什么我们不能为泛型类型分配默认值?

时间:2021-03-12 17:30:47

标签: javascript typescript

以下面的为例。我不太确定错误消息的含义,但从逻辑上讲,签名似乎是完全有效的。 TS 不支持这个吗?

function _createNominalCollection<isOutputOrdered_T extends boolean>(
  input: nominal_T,
  processingFunc: (count: number) => number,
  orderedOutput: isOutputOrdered_T = true,
)

^^^
Type 'boolean' is not assignable to type 'isOutputOrdered_T'.
  'boolean' is assignable to the constraint of type 'isOutputOrdered_T', but 'isOutputOrdered_T' could be instantiated with a different subtype of constraint 'boolean'.ts(2322)

4 个答案:

答案 0 :(得分:8)

正如@VLAZ 指出的那样 _createNominalCollection<false>() 是有问题的。让我们再看看这个错误:

<块引用>

'boolean' 可分配给类型为 'isOutputOrdered_T' 的约束,但 'isOutputOrdered_T' 可以使用约束 'boolean' 的不同子类型实例化。ts(2322)

这意味着你传递了一个显式的 <false> 类型作为泛型参数,isOutputOrdered_T 现在被限制为 false 但是默认参数是 true,它会违反那个。

或者换句话说,truefalseboolean 的子类型,您的函数允许将布尔值限制为这些子类型之一,但不保证对该变量的赋值都属于相同的子类型。

让我提出一个替代方案。


当您有一个基于不同参数返回不同类型的函数时,您应该始终考虑 function overloads 是否更适合建模而不是泛型。它们允许您以一种简单的方式将参数模式专门映射到特定的返回类型,而根本不需要任何泛型。

例如:

// sorted version
function myFn(orderedOutput?: true): 'sorted'

// unsorted version
function myFn(orderedOutput: false): 'unsorted'

// Implementation
function myFn(orderedOutput: boolean = true): 'sorted' | 'unsorted' {
  return orderedOutput ? 'sorted' : 'unsorted'
}

// Testing
const a = myFn(true) // sorted
const b = myFn(false) // unsorted
const c = myFn() // sorted

在这里,您可以为您的函数创建两个签名。第一个“排序”版本不接受任何参数或 true。第二个“未排序”版本接受 false。然后你就有了一个处理这两种情况的实现。

Playground

答案 1 :(得分:2)

您收到此错误的原因是因为 isOutputOrdered_T 可能是像 type truthy = truetype falsy = false 这样的布尔子类型,而不是像 type bool = true | false 这样的完整布尔值。例如:

function _createNominalCollection<T extends boolean>(
  orderedOutput: T
) {}

type booleanSubtype = true;

_createNominalCollection<booleanSubtype>(false);

在这个例子中,编译器会抱怨传递 false,因为 booleanSubtype 只允许 true 作为输入:

<块引用>

“false”类型的参数不能分配给“true”类型的参数

如果是真实的布尔子类型,例如在示例中,您的默认值将不是有效输入,这就是编译器警告您的原因。如果您像这样键入默认值,您的示例可以在没有编译器警告的情况下工作:

function _createNominalCollection<T extends boolean>(
  orderedOutput: T = false as T
) {}

但如前所述,对于真实的子类型,这实际上并不正确,只是消除了错误。

答案 2 :(得分:1)

您可以选择不将默认值指定为 false - 将其保留为未定义。

function f<T extends boolean=false>(a?: T) {
   return a ? 'isTrue' : 'isNotTrue'
}

我想我会问为什么你首先需要这个通用的。返回类型是否根据此标志而改变?如果它确实改变了为什么它需要是单个函数,那么拥有 2 个不同命名的函数不是更有意义吗?

答案 3 :(得分:0)

您收到错误是因为您将 true 作为默认参数分配给 orderedOutput

如果您调用 _createNominalCollection(..,..,false),则约束类型参数 isOutputOrdered_T 将被推断为 false。当然,不允许将值 true 分配给类型为 false 的参数。