Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: ConditionalKeys<Base, Condition> fails when used on Arrays #119

Open
KilianKilmister opened this issue Aug 15, 2020 · 1 comment
Open

Comments

@KilianKilmister
Copy link

KilianKilmister commented Aug 15, 2020

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:

Playground Link

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>

Playground Link

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:

Playground Link

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.
Fund with Polar
@0f-0b
Copy link

0f-0b commented Jun 1, 2023

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];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants