最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Typescript: Why is it that we cannot assign default value to generic type? - Stack Overflow

programmeradmin0浏览0评论

Take the below for instance. I'm not quite sure what the error message means, but it seems that logically, the signature is pletely valid. Is this just not supported by 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)

Take the below for instance. I'm not quite sure what the error message means, but it seems that logically, the signature is pletely valid. Is this just not supported by 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)
Share Improve this question asked Mar 12, 2021 at 17:30 user1543574user1543574 8631 gold badge7 silver badges13 bronze badges 4
  • 3 What if somebody calls _createNominalCollection<false>()? Also, why does this really need to be a generic? – VLAZ Commented Mar 12, 2021 at 17:34
  • 1 it's not clear to me what orderedOutput: isOutputOrdered_T = true, is supposed to be, this looks like assignment, but can't do assignment to a type – Ski Commented Mar 12, 2021 at 17:35
  • 1 The output changes depending on if orderedOutput_T is set. So something like isOutputOrdered_T extends true ? Ta : Tb. The = true is me trying to assign a default value to make the parameter optional (i.e. the output defaults to being ordered) – user1543574 Commented Mar 12, 2021 at 17:37
  • @Ski it's a default value. However, since it's TS, it also includes the type. A simplified version would be (x: number = 41) => x + 1 - x is a parameter which is a number and if not passed in, it's going to get the value 41. – VLAZ Commented Mar 12, 2021 at 18:08
Add a ment  | 

4 Answers 4

Reset to default 9

As @VLAZ pointed out _createNominalCollection<false>() is problematic. Let's look at this error again:

'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)

What that means is that you pass an explicit <false> type as the generic parameter, isOutputOrdered_T is now constrained to false but then the default argument is true, which would violate that.

Or to put it another way, true and false are subtypes of boolean, and your function allows the boolean to be constrained to one of those subtypes, but doesn't guarantee assignments to that variable will all be of that same subtype.

Let me propose an alternative.


When you have a function that returns different types based on different arguments, you should always consider if function overloads are better suited to model that instead of generics. They allow you to specifically map argument patterns to specific return type in a simple way without any generics at all.

For example:

// 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

Here you create two signatures for your function. The first "sorted" version accepts no argument or true. The second "unsorted" version accepts false. Then you have an implementation that handle both cases.

Playground

The reason you get this error, is because isOutputOrdered_T could be a boolean subtype like type truthy = true or type falsy = false instead of being a plete boolean like type bool = true | false. For example:

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

type booleanSubtype = true;

_createNominalCollection<booleanSubtype>(false);

In this example the piler would plain about passing false, because booleanSubtype only allows true as input:

Argument of type 'false' is not assignable to parameter of type 'true'

In case of a truthy boolean subtype, like in the example, your default value wouldn't be a valid input and thats why the piler is warning you. Your example could work without piler warning if you typecast your default value like this:

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

But as shown before, this wouldn't be actually correct with a truthy subtype, just getting rid of the error.

You have option to not assign default to false - leave it as undefined.

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

Thought I'd question why do you need this generic in first place. Does return type change depending on this flag? If it does change why does it need to be single function, would it not make more sense to have 2 distinctly named functions?

You get the error because you assign true to orderedOutput as a default parameter.

If you would call _createNominalCollection(..,..,false) the constrainted type parameter isOutputOrdered_T would be inferred to false. Of course it is not allowed to assign the value true to a parameter of type false.

发布评论

评论列表(0)

  1. 暂无评论