TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

Design Meeting Notes, 10/11/2024

Open DanielRosenwasser opened this issue 1 year ago • 1 comments

Conditional Type Narrowing

#56941

  • Today, we check an expression like

    arg === 1 ? "someString" : 37
    

    by getting the type of both branches and unioning them - and we can't make a determination about how either branch corresponds to the condition.

  • In the experimental PR, each branch is checked the expected type.

    • This is a breaking change, but it catches some desirable breaks.

    • For example:

      // Currently the expression's type is `any` and we check that against `number`,
      // but checked individually, the `string` is correctly caught.
      let x: number = arg === 1 ? "someString" : getAnAny() as any;
      
  • Breaks?

    • Most are true bugs
    • Good chunk are moves in error positions (breaks ts-expect-error)
    • Some unlikely to be real bugs.
  • The motivation was conditional type narrowing - if you think of first principals, you could consider that the conditional expression creates a conditional type.

    • Not too hard to do, but
      • Need to be able to "crack into" each branch of the conditional type for the return statement case as well.

      • You also might not get the "right" conditional type. For example

        function f(x: T): T extends string ? string : number {
            return x === undefined ? someString : someNumber;
        }
        
        • Do you end up synthesizing T extends string ? ... : ... or do you create T extends undefined ? ... : ...?
      • Also, error messages won't be quite as good.

  • Out of time

Slim AST Experiments with Shared Structs in the Compiler

#59992

  • Partially inspired by https://github.com/microsoft/TypeScript/pull/59190

  • Uses flagged functionality for SharedStructs via API (no syntax for shared structs yet).

  • Idea: every Node is has single a fixed layout.

    • Also experimenting with a version that uses shared structs.
  • Separately: a different experiment Uses a "slim AST" which creates a facade to the real AST for API compat.

  • Experimental parser that uses this.

  • You get a speed-up similar to #59190, though it's at the expense of more memory.

    • Much more (why?)
  • If you use shared structs as the backing store for the slim AST, you lose some speed (we anticipate more optimizations with collaboration from V8), but possibly win back some memory and are able to run across multiple threads and you get a net perf win.

    Node Type Allocation Source Time (seconds)
    Current AST Plain objects 1.76
    slim-ast plain objects 1.562
    slim-ast shared structs 2.013
    slim-ast shared structs across 8 workers 1.082

DanielRosenwasser avatar Oct 18 '24 23:10 DanielRosenwasser

Clarification about conditional expression checking: the change in my PR is only for conditional expressions in a return statement, so this will remain the same:

let x: number = arg === 1 ? "someString" : getAnAny() as any; // no error

This will now error:

function fun(arg: number): number {
    return arg === 1 ? "someString" : getAnAny() as any; // error in PR
}

gabritto avatar Oct 18 '24 23:10 gabritto