Skip to content

Commit

Permalink
Update: Pick (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
Harris-Miller authored Oct 2, 2023
1 parent f81c75a commit 3f8e31d
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 28 deletions.
24 changes: 24 additions & 0 deletions test/pick.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expectType, expectError } from 'tsd';

import { pick, KeysAsTuple } from '../es';

const obj = { foo: 1, bar: '2', biz: false };

expectType<{ foo: number; }>(pick(['foo'])(obj));
expectType<{ foo: number; bar: string; }>(pick(['foo', 'bar'])(obj));
expectType<{ foo: number; bar: string; biz: boolean }>(pick(['foo', 'bar', 'biz'])(obj));
expectError(pick(['baz', 'bar', 'biz'])(obj));

expectType<{ foo: number; }>(pick(['foo'], obj));
expectType<{ foo: number; bar: string; }>(pick(['foo', 'bar'], obj));
expectType<{ foo: number; bar: string; biz: boolean }>(pick(['foo', 'bar', 'biz'], obj));
expectError(pick(['baz', 'bar', 'biz'], obj));

// Record
expectType<Record<string, number>>(pick(['foo', 'bar'], {} as Record<string, number>));

const names: string[] = ['foo', 'bar'];
// in cases where names is either `string[]` or `(keyof obj)[]`, cast with supplied `KeysAsTuple` type function
expectType<typeof obj>(pick(names as KeysAsTuple<typeof obj>, obj));
// this case however is inaccurate, best to cast as a `Partial` in a real-world scenario
expectType<Partial<typeof obj>>(pick(names as KeysAsTuple<typeof obj>, obj) as Partial<typeof obj>);
32 changes: 4 additions & 28 deletions types/pick.d.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,5 @@
import * as _ from 'ts-toolbelt';
import { ElementOf } from './util/tools';

export function pick<const Names extends readonly [PropertyKey, ...PropertyKey[]]>(names: Names): <U extends Record<ElementOf<Names>, any>>(obj: U) => string extends keyof U ? Record<string, U[keyof U]> : Pick<U, ElementOf<Names>>;
export function pick<U, const Names extends readonly [keyof U, ...(keyof U)[]]>(names: Names, obj: U): string extends keyof U ? Record<string, U[keyof U]> : Pick<U, ElementOf<Names>>;

export function pick<T extends readonly [any, ...any], K extends string | number | symbol>(
names: readonly K[],
array: T,
): {
[P in K as P extends number
? _.N.Greater<T['length'], P> extends 1
? P
: never
: never]: P extends keyof T ? T[P] : T[number];
};
export function pick<T, K extends string | number | symbol>(
names: readonly K[],
obj: T,
): { [P in keyof T as P extends K ? P : never]: T[P] };
export function pick<K extends string | number | symbol>(
names: readonly K[],
): <T extends readonly [any, ...any] | object>(
obj: T,
) => T extends readonly [any, ...any]
? {
[P in K as P extends number
? _.N.Greater<T['length'], P> extends 1
? P
: never
: never]: P extends keyof T ? T[P] : T[number];
}
: { [P in keyof T as P extends K ? P : never]: T[P] };
15 changes: 15 additions & 0 deletions types/util/tools.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,3 +500,18 @@ export type WidenLiterals<T> =
: T extends number
? number
: T;

/**
* Extract the types from an array
* Works with Tuples, eg `ElementOf<typeof ['p1', 'p2']>` => `'p1' | 'p2'`
*
* <created by @harris-miller>
*/
export type ElementOf<Type extends readonly any[]> = Type extends readonly (infer Values)[] ? Values : never;

/**
* Convenance type function to extract keys of an object as a tuple literal
*
* <created by @harris-miller>
*/
export type KeysAsTuple<T> = [keyof T, ...(keyof T)[]];

0 comments on commit 3f8e31d

Please sign in to comment.