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, 6/2/2021 #44400

Closed
DanielRosenwasser opened this issue Jun 2, 2021 · 0 comments
Closed

Design Meeting Notes, 6/2/2021 #44400

DanielRosenwasser opened this issue Jun 2, 2021 · 0 comments
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

Generalized Index Signatures

#26797

  • A couple of asks:

    • I can't declare symbol indexers
    • I can't declare template string signatures.
    • I can't declare indexers that are union types.
    • I can't declare indexers that are enum types.
  • We don't want to have arbitrary indexers - don't like the idea of generics that get instantiated.

  • Also, we want to preserve "round-trippability"

    type A = Record<string, "hello">; // { [x: string]: "hello" }
    type B = Record<number, "world">; // { [x: number]: "world" }
    type C = Record<"a", "hello">;    // { a: "hello" }
    
    
    // imagine
    
    interface Foo {
        [x: "a" | "b" | "c"]: number
    }
  • What's the keyof?

type FooKeys = keyof Foo; // "a" | "b" | "c"
  • What if you feed that back into Record?
type FooReconstructed = Record<FooKeys, number>;
  • Are these properties? Do you preserve the index signature?

    • Starting smaller means we don't have to concern ourselves with this problem.
  • How does this compare with computed properties?

    interface Yadda {
        ["propertyName"]: number;
    }
    • Could imagine that this would be valid

      type Foo = {
          ["hello" | "world"]: number
      }

      desugars to

      type Foo = {
          ["hello"]: number;
          ["world"]: number;
      }

      and

      type Foo = {
          [string]: number
      }

      desugars to

      type Foo = {
          [x: string]: number
      }
  • Use-case: string enum index signature

    enum E {
        A = "A",
        B = "B",
    }
    
    interface Yadda {
        ["A" | "B"]: boolean;
    }
    • What does this do? Index signature? Property?
  • Idea: start with just new index signature types (i.e. symbol, template string), and come back to this problem.

  • With a mapped type like Record, we have a lot of subtlety on how to construct an object type:

    • string, number become index signatures
    • Individual unique keys like "hello" and "world" each get their own properties like hello and world.
    • Generics - we don't create keys for them.
  • We have freedom to grow in the space of creating a property with a union type.

    • There's definitely some "strangeness".
    • declare const q: "hello" | "world";
      interface A {
        ["hello" | "world"]: number; // this could create several optional properties
      }
    • declare const q: "hello" | "world";
      interface A {
        [q]: number; // this could create several optional properties OR several known properties
      }
    • declare const q: "hello" | "world";
      // this could create several optional properties
      // OR a union of several object types
      let obj = {
        [q]: 123;
      }
      interface ObjType {
          [typeof q]: number
      }
      • You'd hope that obj is compatible with ObjType.
  • "Can I have arbitrary index signatures?" = one of several ideas, not all which are consistent.

  • Patterns in index signatures

    • Now we can cover that with template literal index signatures.
    • Mapped types can preserve these consistently.
  • A thing that I can only index with an enum type.

    • You can do Record<SomeEnum, SomeType>, but we erase the "enuminess" - just create keys based on the underlying runtime values.
    • Not strict, but kind of weird to differentiate these - if E.A is equal to 1, then why does it get its own key in a union? Weird.
    • enum E { A = 0, B = 1 }
      type Foo = Record<E, number>;
      declare let x: Foo;
      
      x[E.A] // okay - yay!
      x[0]   // want this to not be okay - neigh!

Object.hasOwn

#44253

  • A "static" version of Object.prototype.hasOwnProperty.
    • Object.create(null).hasOwnProperty
    • People usually instead write Object.prototype.hasOwnProperty.call(...)
  • [[Bike-shedding on how it should look in lib.d.ts]]
  • hasOwn should not make your types worse.
  • Issues which revisits behavior of last topic - always fall back to an index signature if a property is not found.
  • Need for a "rest" index signature in some ways.
  • What about the differences between in and hasOwn?
    • They're there, but probably not worth special-casing the control flow behavior.
  • Writing as a type guard has too many nuances, subtleties.
  • We would like to be able to have a type signature annotation to convey what hasOwn does, but there's complexity in that. Leaning towards a syntactic construct.
  • People are not always polyfilling on globals due to frozen realms etc.
  • hasKey<T, K extends any>(x: T, k: K): K in T?
  • Revisit.
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Jun 2, 2021
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

1 participant