One interesting observation I had is that in the example below, I was successful in extracting explicit keys from the object, but had difficulty enforcing its values:
const myCollection = {
a: {test: 1},
b: {test: 2},
c: {text: 3} // no error
};
type MyCollectionKeys = keyof typeof myCollection;
// MyCollectionKeys is 'a' | 'b' | 'c'
However, when attempting to enforce the values as shown below, the explicit keys were lost:
type CollectionValue = {test: number};
const myCollection: ({[k: string]: CollectionValue}) = {
a: {test: 1},
b: {test: 2},
c: {text: 3} // Type '{ text: number; }' is not assignable to type 'CollectionValue'.
};
type MyCollectionKeys = keyof typeof myCollection;
// MyCollectionKeys is string | number
I came up with a workaround which involves proxying the object like this:
type CollectionValue = {test: number};
const _myCollection = {
a: {test: 1},
b: {test: 2},
c: {text: 3}
};
type MyCollectionKeys = keyof typeof _myCollection;
// MyCollectionKeys is 'a' | 'b' | 'c'
const myCollection: ({[k in MyCollectionKeys]: CollectionValue}) = _myCollection;
// Type '{ a: { test: number; }; b: { test: number; }; c: { text: number; }; }' is not assignable to type '{ a: CollectionValue; b: CollectionValue; c: CollectionValue; }'.
// Types of property 'c' are incompatible.
// Property 'test' is missing in type '{ text: number; }' but required in type 'CollectionValue'
There are a couple of drawbacks with this method:
- You need to have a proxy object
- If you violate the type, the error is displayed at the assignment rather than within the original object at the problematic key
Do you know of a more graceful solution to this issue?