Description
Generalized Index Signatures
-
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.
- I can't declare
-
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 likehello
andworld
. - 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 withObjType
.
- You'd hope that
-
"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 to1
, 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!
- You can do
Object.hasOwn
- 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
andhasOwn
?- 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.