Experiment with making ExactlyOne util type more lenient
Area: SpecificationCategory: Bug
In discussing with @afharo and @rudolf from Kibana Core, the `ExactlyOne<T>` utility type added in 9.3 to support mutually exclusive keys (aka "containers") from the Elasticsearch spec [requires a lot of patches](https://github.com/elastic/kibana/pull/253660) to Kibana before they can upgrade the client.
As noted in that PR:
> 1. `keyof` resolves to `never` — Since undefined is part of the union, `keyof QueryDslQueryContainer` becomes `never`, breaking any `PickQueryDslQueryContainer, ...` or `keyof QueryDslQueryContainer` usage.
> 2. Properties are "possibly undefined" — Any variable typed as one of these containers is now possibly undefined, so accessing `.bool`, `.filter`, etc. triggers TS18048.
3. Indexed access fails — `QueryDslQueryContainer['match_phrase']` fails because the key can't be resolved on a union that includes `undefined`.
There may be ways to make `ExactlyOne` more lenient while still helping to enforce the mutual exclusivity. A couple ideas:
This would allow `undefined` instead of `never`:
```typescript
type ExactlyOne<T> = {
[K in keyof T]: Pick<T, K> & Partial<Record<Exclude<keyof T, K>, undefined>>
}[keyof T]
```
This removes the `?` on the `never` properties, changing the behavior of `Exclude` slightly:
```typescript
type ExactlyOne<T> = {
[K in keyof T]: { [P in K]: T[P] } & { [P in Exclude<keyof T, K>]: never }
}[keyof T]
```
Etc. etc. The key will be running [the Kibana buildkite pipeline](https://github.com/elastic/elasticsearch-specification/blob/main/.buildkite/kibana.yml) from the spec repo to validate whether any alternative solution allows us to enforce container rules without creating extra noise for Kibana.
3 条评论