5

Consider this example:

function foo<T>(t: T): boolean {
  return t === 1;
}

I get This condition will always return 'false' since the types 'T' and 'number' have no overlap..

The error goes away if I change it to:

function foo<T extends any>(t: T): boolean {
  return t === 1;
}

I thought T could be any by default, so why did I need extends any? Without extends any, what type does TS expect T to be?

Edit, actual function:

function cleanObj<T>(
  obj: ObjectOf<T>,
): ObjectOf<T> {
  const newObj = {};
  for (const k of Object.keys(obj)) {
    if (obj[k] !== null && typeof obj[k] !== 'undefined' && obj[k] !== false) {
      newObj[k] = obj[k];
    }
  }

  return newObj;
}
5
  • 1
    I wouldn't say its any by default, it's behaviour is more like unknown which for your case, would also fail because you can't compare to a type that you're not aware of. As @MoxxiManagarm said, if you don't need the generics, just get rid of them. Commented Sep 8, 2020 at 7:03
  • I do need the generic, but there's no need to paste a long function when 3 lines suffice to explain the problem Commented Sep 8, 2020 at 8:01
  • At compile time the type of T would be known, isn't that the whole point of generics? Even if it's unknown, the error "always return 'false'" is still wrong, since unknown doesn't mean it can't be a number Commented Sep 8, 2020 at 8:03
  • 2
    @MikeS. except <T extends unknown> works fine, the same as extends any Commented Sep 8, 2020 at 8:08
  • @MingweiSamuel oops, my bad, yes you can compare to unknown, you just can't assign anything to it. Commented Sep 8, 2020 at 8:14

2 Answers 2

4

The triple equals operator === returns true if both operands are of the same type and contain the same value. Compared to that "1" == 1 would return true.

Since you use === you also compare the type, which is number for the right hand operant 1. However, your T can not just be a number, that is why the compiler gives you this notification.

Possible solutions:

  • remove the generics if not really necessary
  • use == instead of ===
  • (possible further approaches depending on your actual code)
Sign up to request clarification or add additional context in comments.

3 Comments

This doesn't explain why explicitly doing T extends any fixes it, since number != any.
@LeoJiang No, but any can very well be a number
I still don't get the answer at all. I'll paste my full function. My question is basically what's the difference between <T> and <T extends any>, I thought they should be the same thing
3

Partially taken from https://github.com/microsoft/TypeScript/issues/17445 :

Consider your original function:

function compare<T>(obj: T): boolean {
  return obj === 1;
}

if it would be allowed to compare anything to T we could also write:

function compare<T>(obj: T): boolean {
  return obj === 'some-string-thats-definitely-not-obj';
}

If you use that function inside of some if, you could potentially create unreachable code that won't throw an error.

This is why typescript requires you to explicitely tell it that T can be any, which is obviously unsafe for the above reason.

3 Comments

Wouldn't a more reasonable solution to be to treat <T> as an implicit any? I.e. it'll cause an error with noImplicitAny on, but by default it's the same as <T extends any>
No, T shouldn't be able to be compared to like any or unknown. In the case of generics extending any, it wouldn't be worse to just set the parameter to any itself.
As for a solution, I think the recommended thing to do is to just upcast 1 to {} or unknown, if you know what you're doing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.