Skip to content

Design Meeting Notes, 7/22/2022 #50008

Description

Inference Change Between Unions of undefined

#49938

Lots of examples

Here's a minimal break.

typeB={foo: string,};typeD={foo: string,bar: number};declarefunctionequals<T>(a: T,b: T): boolean;functionf(b: B,d: D|undefined){// Used to work, now infers `B` and fails.if(equals(b,d)){}}

Playground link

Narrowing Changes Against Top-ish Types

#49988

  • The isMap type predicate now narrows values to a Map<...> | ReadonlyMap<...> instead of Map<...> if that's what you already had.

  • Introduced by Improve narrowing logic for instanceof, type predicate functions, and assertion functions #49625

  • Start with this example.

    typeFalsy=false|0|0n|''|null|undefined;declarefunctionisFalsy(value: unknown): value is Falsy;functionfx1(x: string|number|undefined){if(isFalsy(x)){x;// "" | 0 | undefined, previously undefined}}
  • But people depend on the old behavior.

  • This is now different

    declarefunctionisMap<T>(object: T|{}): object is TextendsReadonlyMap<any,any> ? (unknownextendsT ? never : ReadonlyMap<any,any>) : Map<unknown,unknown>;declareconstromor: ReadonlyMap<any,any>|Record<any,any>if(isMap(romor)){romor;// Previously `ReadonlyMap<any, any>`, now `ReadonlyMap<any, any> | Map<any, any>`}
  • But ReadonlyMap<...> | Map<...> just provides all the same methods as ReadonlyMap<...>.

    • Are they sufficiently identical between their methods?
    • Often these signatures are not, which makes them effectively uncallable.
    • We think yes.
  • The isObject case is worse.

    • Previous behavior was weird! (see playground)

      declarefunctionisObject(value: unknown): value is Record<string,unknown>;functionf1(obj: {}){if(isObject(obj)){// Worked, obj is narrowed to Record<string, unknown>obj["stuff"]}}functionf2(obj: {}|undefined){if(isObject(obj)){// Doesn't work obj is not narrowed.obj["stuff"]}}
    • Now neither example works. (see playground)

    • Will need to think through these.

  • WeakRef example

    • Very incorrect. T is never witnessed, and WeakRef is supposed to be a proxy around T itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design NotesNotes from our design meetings

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions