Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design Meeting Notes, 9/14/2022 #51303

Closed
DanielRosenwasser opened this issue Oct 25, 2022 · 0 comments
Closed

Design Meeting Notes, 9/14/2022 #51303

DanielRosenwasser opened this issue Oct 25, 2022 · 0 comments
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

in Operator Checking Improvements

#50666

  • The in operator is a little bit funny.
    • 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?
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Oct 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Notes Notes from our design meetings
Projects
None yet
Development

No branches or pull requests

2 participants