type-fest
type-fest copied to clipboard
Bug: ConditionalKeys<Base, Condition> fails when used on Arrays
There is a bug in Typescript related to keyof Array
that affects ConditionalKeys<Base, Condition>
and the Types using it. (reporting the bug itself to typescript, aswell)
It can be fixed adHoc without affecting any non.bugged existing uses
case:
type Arr = readonly [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
type WhoopMyArray_Faulty<Base extends ArrayLike<any>, Condition> = {
[Key in keyof Base]:
Base[Key] extends Condition
? 'Whoop it'
: Base[Key] extends Base[number] ? Base[Key] : never
}
// action is performed on array elements but includes all values of Array.prototype
type WhoopedArrayKeys = WhoopMyArray_Faulty<Arr, 7 | 5 | 2>[keyof Arr]
So using ConditionalKeys<Base, Condition>
import { ConditionalKeys } from 'type-fest'
type WhoopedArray = readonly [0, 1, 'Whoop it', 3, 'Whoop it', 5, 'Whoop it', 7, 8, 9]
type WhoopedArrayKeys = ConditionalKeys<WhoopedArray, 'Whoop it'>
results in "2" | "4" | "6"
and all Array.prototype-values
this can be fixed by adding a temporary dummy key to the UtilityType:
type WhoopedArray = [0, 1, 'Whoop it', 3, 'Whoop it', 5, 'Whoop it', 7, 8, 9]
export type ConditionalKeys<Base, Condition> = NonNullable< // TODO: report bug in ts as type-fest
// Wrap in `NonNullable` to strip away the `undefined` type from the produced union.
{
// Map through all the keys of the given base type. NOTE: dummy-prop is needed for Arrays
[Key in keyof Base | '__im-a-dummy-prop__']:
// Pick only keys with types extending the given `Condition` type.
Base[Exclude<Key, '__im-a-dummy-prop__'>] extends Condition
// Retain this key since the condition passes.
? Key
// Discard this key since the condition fails.
: never
// Convert the produced object into a union type of the keys which passed the conditional test.
}[keyof Base]
>;
type WhoopedArrayKeys = ConditionalKeys<WhoopedArray, 'Whoop it'>
// -> '2' | '4' | '6'
Took me ages to find out why my stuff isn't working
Upvote & Fund
- We're using Polar.sh so you can upvote and help fund this issue.
- The funding will be given to active contributors.
- Thank you in advance for helping prioritize & fund our backlog.
This fix does not involve an arbitrary dummy key.
export type ConditionalKeys<Base, Condition> = {
// Map through all the keys of the given base type.
[Key in Exclude<keyof Base, never>]:
// Pick only keys with types extending the given `Condition` type.
Base[Key] extends Condition
// Retain this key since the condition passes.
? Key
// Discard this key since the condition fails.
: never;
// Convert the produced object into a union type of the keys which passed the conditional test.
}[keyof Base];