3

I ran across some code that looked completely wrong in a repo I'm working on. Below is an example of what I came across.

interface Car {
  make: string
  model: string
}

type SomeType = Partial<Car>

const car: SomeType = {}

if (car === "typescript doesn't complain about this") {
    // This code will never execute..?
}

Am I missing something? car will never be equal to a string, right? Normally when you try to compare two different types that have no overlap Typescript will yell at you and tell you it will always return false. Any help on this would be greatly appreciated!

haymez
  • 215
  • 1
  • 8

2 Answers2

3

Partial<Car> is a weak type because all its properties are optional. TypeScript 2.4 added a check that would warn you if you tried to assign something like a string to a weak type, since there are no overlapping properties:

let car: { make?: string, model?: string };
car = {}; // okay
car = "oops"; // error! Type '"oops"' has no properties in common with type 
// '{ make?: string | undefined; model?: string | undefined; }'.

But before TypeScript 2.4, car = "oops" would have been allowed with no error, because none of the apparent members of "oops" (like length and toUpperCase) conflict with Partial<Car>. Structurally speaking, a string is a Partial<Car>. But the assignment is probably an mistake and weak type detection warns you about it.


This weak type detection only seems to apply to assignability, though. It doesn't kick in for comparison operators, as you've noticed:

if (car === "oops") {  } // no error

There is an open feature request at microsoft/TypeScript#32627 asking for this to be changed. If you care enough about this to see it changed, you might go to that issue, give it a , and maybe give a compelling case for why this matters to you. But I doubt it would have much of an effect, and there's no indication this will be implemented since there's no community engagement with it. Presumably people don't run into this situation very often in real world code.


So the answer to your question as asked is: weak type detection was never implemented for comparison operators, and there doesn't seem to be a lot of demand for this to change.

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • Interesting. Thanks for the detailed answer! Your explanation definitely makes sense. I'll visit the linked feature request and give it some engagement. The current behavior with comparison operators seems to be more confusing than helpful. – haymez Aug 29 '22 at 15:48
0

Partial<T> will always allow the type {} (when no keys are provided).

{} has some... quirks.

It's almost like using any. Almost every value in JavaScript is assignable to type {}. Because think about it. It doesn't require the value to have any keys. Anything at least has some keys, so by structural typing, anything conforms to this type.

See if you leave out SomeType, it will be the same result:

const car = {}; // now inferred as '{}'

if (car === "typescript doesn't complain about this") {

}

TypeScript doesn't complain about it because in its eyes, it is possible for the condition to run (types overlap, strings are assignable to {}).

kelsny
  • 23,009
  • 3
  • 19
  • 48
  • While you can assign anything to the type `{}`, you can't assign a `string` to `Partial`. This seems more like a bug to me as I could not find any way for assignment wich makes the comparison evaluate to `true` without type assertions. – Tobias S. Aug 29 '22 at 15:21