Consider the code snippet provided in this playground:
class A {
private z = 0
}
type K = "z"
type ValidKeys = A[K] extends any ? K : never
The type ValidKeys
compiles correctly and matches K
only when K
represents a subset of keys from A
. It will not compile if additional keys are added to K
like:
type K = "z" | "not-a-key"
This behavior is desired, but I am looking to make ValidKeys
generic based on A
and K
:
type ValidKeys<A, K> = ...
How can I achieve this type definition?
Why is it necessary?
When working with mobx, the documentation states:
- Private fields cannot be annotated by default in TypeScript. To overcome this, relevant private fields need to be explicitly passed as generic arguments, like so:
makeObservable<MyStore, "privateField" | "privateField2">(this, { privateField: observable, privateField2: observable })
Hence, I require passing a type containing specific private keys to a generic function. Using literals may lead to runtime issues if properties are renamed.
Instead of:
constructor() {
makeObservable<Store, "x" | "y">(this, { x: observable, y: false })
}
I aim to write:
constructor() {
makeObservable<Store, ValidKeys<Store, "x" | "y">>(this, { x: observable, y: false })
}
To catch key renaming errors at compile-time.
The current workaround involves manually adding a snippet within the constructor:
constructor() {
type ExtraKeys = "x" | "y"
type _CompilesIfOk_ExtraKeys = Store[ExtraKeys]
makeObservable<Store, ExtraKeys>(this, { x: observable, y: false })
}
I am seeking a simpler solution for this and intend to move it into a helper function.