Skip to content

Commit

Permalink
fix(pluck): pluck now can return object (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nemo108 authored Jan 20, 2024
1 parent 5413dd2 commit fd618c0
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 31 deletions.
103 changes: 78 additions & 25 deletions test/pluck.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,86 @@ import { expectType, expectError } from 'tsd';
import { __, pluck } from '../es';

type Obj = { name: string; age: number };
type Rec = {
a: {name: string, age: number},
b: {name: string, age: number, desc: string}
};
type ConstRec = {
readonly a: {name: 'foo', age: 13},
readonly b: {name: 'bar', age: 31, desc: 'barrr'}
};

// pluck(key, list)
// pluck(key)
const getFirstItems = pluck(0);
const getName = pluck('name');
const getAge = pluck('age');
const getNope = pluck('nope');

// pluck(key)(list::Array[])
expectType<string[]>(getFirstItems([] as string[][]));
expectType<number[]>(getFirstItems([] as number[][]));
expectType<1[]>(getFirstItems([] as 1[][]));
expectType<Obj[]>(getFirstItems([] as Obj[][]));
expectError(getFirstItems('string')); // works in JS, but should not be valid in TS
expectError(getFirstItems({} as Obj));


// pluck(key)(list::Record[])
expectType<string[]>(getName([] as Obj[]));
expectType<number[]>(getAge([] as Obj[]));
expectError(getNope([] as Obj[]));
expectError(getFirstItems([] as Obj[]));


// pluck(key)(record::Record)
expectType<{ a: string, b: string }>(getName({} as Rec));
expectType<{ a: number, b: number }>(getAge({} as Rec));
expectType<{ readonly a: 'foo', readonly b: 'bar' }>(getName({} as ConstRec));
expectError(getNope({} as Rec));
expectError(pluck('desc')({} as ConstRec)); // works in JS, but not valid in TS


// pluck(key, list::Record[])
expectType<string[]>(pluck('name', [] as Obj[]));
expectType<number[]>(pluck('age', [] as Obj[]));
expectError(pluck('nope', [] as Obj[]));
expectError(pluck(0, [] as Obj[]));


// pluck(key, list::Array[])
expectType<string[]>(pluck(0, [] as string[][]));
expectType<number[]>(pluck(1, [] as number[][]));
expectType<1[]>(pluck(0, [] as 1[][]));
expectType<Obj[]>(pluck(0, [] as Obj[][]));
expectError(pluck(0, 'string')); // works in JS, but should not be valid in TS
expectError(pluck(0, {} as Obj));


// pluck(key, record::Record)
expectType<{ a: string, b: string }>(pluck('name', {} as Rec));
expectType<{ readonly a: 'foo', readonly b: 'bar' }>(pluck('name', {} as ConstRec));
expectType<{ a: number, b: number }>(pluck('age', {} as Rec));
expectError(pluck(1, {} as Rec));
expectError(pluck('nope', {} as Rec));
expectError(pluck('desc', {} as ConstRec)); // works in JS, but not valid in TS


// pluck(__, list::Record[])(prop)
const getFromObjList = pluck(__, [] as Obj[]);

expectType<string[]>(getFromObjList('name'));
expectType<number[]>(getFromObjList('age'));
expectError(getFromObjList('nope'));


// pluck(__, list::Array[])(prop)
expectType<string[]>(pluck(__, [] as string[][])(0));
expectType<number[]>(pluck(__, [] as number[][])(1));
expectError(pluck(__, [] as Obj[])(0));


// pluck(__, record::Record)(prop)
expectType<{ a: string, b: string }>(pluck(__, {} as Rec)('name'));
expectType<{ a: number, b: number }>(pluck(__, {} as Rec)('age'));
expectError(pluck(__, {} as Rec)('nope'));

// pluck(key)(list)
expectType<string[]>(pluck('name')([] as Obj[]));
expectType<number[]>(pluck('age')([] as Obj[]));
expectError(pluck('nope')([] as Obj[]));

// pluck(__, list)(prop)
expectType<string[]>(pluck(__, [] as Obj[])('name'));
expectType<number[]>(pluck(__, [] as Obj[])('age'));
expectError(pluck(__, [] as Obj[])('nope'));

// pluck(key, record)
expectType<string[]>(pluck('name', {} as Record<string, Obj>));
expectType<number[]>(pluck('age', {} as Record<string, Obj>));
expectError(pluck('nope', {} as Record<string, Obj>));

// pluck(key)(record)
expectType<string[]>(pluck('name')({} as Record<string, Obj>));
expectType<number[]>(pluck('age')({} as Record<string, Obj>));
expectError(pluck('nope')({} as Record<string, Obj>));

// pluck(__, record)(prop)
expectType<string[]>(pluck(__, {} as Record<string, Obj>)('name'));
expectType<number[]>(pluck(__, {} as Record<string, Obj>)('age'));
expectError(pluck(__, {} as Record<string, Obj>)('nope'));
15 changes: 9 additions & 6 deletions types/pluck.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Placeholder } from 'ramda';

export function pluck<K extends PropertyKey>(prop: K extends Placeholder ? never : K): {
<U extends readonly unknown[] | Record<K, any>>(obj: Record<PropertyKey, U>): U extends readonly (infer T)[] ? T[] : U extends Record<K, infer T> ? T[] : never;
<U extends readonly unknown[] | Record<K, any>>(list: U[]): U extends readonly (infer T)[] ? T[] : U extends Record<K, infer T> ? T[] : never;
<U extends O[keyof O], UK extends keyof U, O extends Record<string, any>>(obj: K extends UK ? O : never): { [OK in keyof O]: O[OK][K] };
<U extends readonly unknown[] | Record<K, any>>(list: readonly U[]): U extends readonly (infer T)[] ? T[] : U extends Record<K, infer T> ? T[] : never;
};
export function pluck<U>(__: Placeholder, obj: Record<PropertyKey, U>): <K extends keyof U>(prop: K) => Array<U[K]>;
export function pluck<U>(__: Placeholder, list: readonly U[]): <K extends keyof U>(prop: K) => Array<U[K]>;
export function pluck<K extends keyof U, U>(prop: K, obj: Record<PropertyKey, U>): Array<U[K]>;
export function pluck<K extends keyof U, U>(prop: K, list: readonly U[]): Array<U[K]>;
export function pluck<U>(__: Placeholder, list: readonly U[]): <K extends keyof U>(prop: U extends readonly any[] ? number : K) => U extends readonly (infer T)[] ? T[] : U extends Record<K, infer T> ? T[] : never;
export function pluck<U extends O[keyof O], O extends Record<string, any>>(__: Placeholder, record: O): <K extends keyof U>(prop: K) => { [KK in keyof O]: O[KK][K] };
export function pluck<
K extends keyof U,
U extends C[keyof C extends string ? keyof C : number],
C extends { [k: string]: Record<string, any> } | ReadonlyArray<Record<string, any> | readonly any[]>
>(prop: K, collection: C): keyof C extends string ? { [CK in keyof C]: C[CK] extends Record<K, any> ? C[CK][K] : never } : Array<U[K]>;

0 comments on commit fd618c0

Please sign in to comment.