You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
You can't use it with a right operand of null/undefined - sure.
"foo" in null - errors
But you can't use it on primitives either.
"foo" in 1234 - also errors
Strangely, you can use it with non-key values on the left side.
[] in { prop: 42 } works.
This PR tries to adds some of those restrictions in the type-checker.
Disallows generic unconstrained keys, requires them to be bounded by at least string | number | symbol.
Ensures the right side is assignable to object
It also improves how we do narrowing - and that's the highlight for most users.
Our existing narrowing logic does something unsound which is based on intent.
If you use in on a union of objects, we remove the objects that don't explicitly declare the appropriate keys.
The new narrowing logic is that if no constituent of a union named that property, then we try intersecting each constituent with Record<TypeOfKey, unknown>.
Actually, this isn't special to union types. With the new PR, if you have a non-union type, we do the same intersection narrowing.
As a principle, we should avoid doing something different in narrowing depending on if the narrowed type is/isn't an intersection.
There's some work we've done to ensure that narrowing from unknown is stricter, and TypeScript makes sure that something like value && "foo" in value accounts for the possibility of "foo" in 1234.
When you have value: unknown and you use it in a truthy check like in value && "foo" in value, we will actually narrow value to a special version of {} that knows it was narrowed that way from unknown, and shouldn't be assignable to object until you see typeof value === "object".
How does this work with patterns like "href" in elem (where elem: HTMLElement)?
Yes.
If we did this all again today, we might not have made {} only compatible with objects, made a separate nonprimitive type.
Questionable.
Unconstrained type + {} restrictions have sort of opened up pandora's box, and it is hard to explain the interaction between these types.
Most people didn't know and didn't need to know - now it's getting in people's way.
Also, the records and tuples proposal will only exacerbate this.
How does this work with typeof value === "function?
Yes, but also don't need truthiness in those cases (thanks null)
Intersecting with Record<"foo", unknown> introduces a new property of type unknown.
That's the least capable type to read from - but to write to, you can write anything.
That's the flip side. Youl could accidentally overwrite a property.
Maybe it needs to be a readonly property?
The text was updated successfully, but these errors were encountered:
in
Operator Checking Improvements#50666
in
operator is a little bit funny.null
/undefined
- sure."foo" in null
- errors"foo" in 1234
- also errors[] in { prop: 42 }
works.string | number | symbol
.object
in
on a union of objects, we remove the objects that don't explicitly declare the appropriate keys.Record<TypeOfKey, unknown>
.unknown
is stricter, and TypeScript makes sure that something likevalue && "foo" in value
accounts for the possibility of"foo" in 1234
.value: unknown
and you use it in a truthy check like invalue && "foo" in value
, we will actually narrowvalue
to a special version of{}
that knows it was narrowed that way fromunknown
, and shouldn't be assignable toobject
until you seetypeof value === "object"
."href" in elem
(whereelem: HTMLElement
)?{}
only compatible with objects, made a separatenonprimitive
type.{}
restrictions have sort of opened up pandora's box, and it is hard to explain the interaction between these types.typeof value === "function
?null
)Record<"foo", unknown>
introduces a new property of typeunknown
.readonly
property?The text was updated successfully, but these errors were encountered: