Skip to content

Design Meeting Notes, 3/29/2019 #30645

Closed
Closed
@RyanCavanaugh

Description

@RyanCavanaugh

Design Meeting Notes 3/29/2019

Safeness of indexed access types (#30603)

This code is patently unsafe:

function f2(obj: { a: number, b: string }, key: 'a' | 'b') {
    obj[key] = 123;
}

We need to get different types for reading and writing an indexed access type.

Also has implications for constraints and assignability

Consider this code:

function foo<T, K extends keyof T>(obj: T, key: K) {
    obj[key] = 123;
}

From a constraint perspective, the correct way to look at a reduction of T[K] is:

  • If T extends C, then we try to resolve T[K] using the constraint (possibly recursively)
    • Better form: During simplification, specify whether this is a simplification for reading or writing
      • Simplification today: T[A | B] simplifies to T[A] | T[B], which is only correct for reading
      • Similar: Given (T | U)[K], the read simplification is T[K] | U[K] and the write simplification is T[K] & U[K]
    • During assignability checks, simplify the source for reading and the target for writing
    • Once the unions, etc are removed via simplification, we still might have constraints
      • e.g. T[K] = S where T extends { a: string, b: number } and K extends keyof T
      • We need to pick the widest K ("a" | "b") and then distribute that (T['a'] | T['b']]) into the narrowest target value
      • Seems to work pretty well
    • In the unconstrained T case, what do we do?
      • Disallow assignment
    • What about T extends { [x: string]: any } ?
      • Is the intent that any assignment is legal here?
        • Seems bad?
    • What about T extends { [x: string]: number } ?
      • All writes via constraints are suspect...
        • Equally suspect?
      • Is T[""] = 0 OK or no?
        • 🤷‍
        • This is a subtyping violating if it's inconsistent with T[K] = 0

#30637

function print<T>(x: T): void {
    console.log(x.toString());
}

print(null); // A-OK!
  • This was OK because the default constraint of T is {} and {} acquires the Object apparent members.
    • (Non-SNC back compat accomplished by adding the Object apparent members back to unknown)
  • Now you have to write print<T extends {}> which becomes meaningful.
  • Some breaks in react-redux
  • lodash has some breaks where they seem to rely on {} coming from broken inference (wow)
    • Most changes are actually just different error spans
  • Back compat means what
    • Fix lodash and react-redux
    • Circular constraints keep hobbling on
  • Should we lint-ban {} in DT (in files with sufficiently-high version)?
    • Yes (use unknown or object or string | number | boolean | object | symbol)
    • "Goodbye {}, we hardly knew ye" - @rbuckton

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