Skip to content

Commit f734c70

Browse files
committed
fix(propEq): improve propEq typings
* remove unnecessary constraint from propEq value: the function should receive any type values of object, no matter what parameter it received; * add additional types to use with placeholder
1 parent 4d4f2ea commit f734c70

3 files changed

Lines changed: 133 additions & 19 deletions

File tree

test/anyPass.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ expectType<boolean>(
2525
})
2626
);
2727

28-
expectError(
28+
expectType<boolean>(
2929
isVampire({
3030
age: 21,
3131
garlic_allergy: true,

test/propEq.test.ts

Lines changed: 101 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,127 @@
11
import { expectError, expectType } from 'tsd';
22

3-
import { propEq } from '../es';
3+
import {__, propEq} from '../es';
44

55
type Obj = {
66
union: 'foo' | 'bar';
77
str: string;
8-
num: number;
8+
int: number;
9+
numLike: number | `${number}`;
10+
optional?: string;
11+
nullable: string | null;
912
u: undefined;
1013
n: null;
1114
};
15+
type NumArr = number[];
1216

17+
// ######################
1318
// propEq(val, name, obj)
1419
expectType<boolean>(propEq('foo', 'union', {} as Obj));
15-
// non-union string fails
16-
expectError(propEq('nope', 'union', {} as Obj));
17-
// completely different type fails
18-
expectError(propEq(2, 'union', {} as Obj));
20+
// propEq doesn't create unnecessary values constraint for object
21+
expectType<boolean>(propEq('1', 'union', {} as Obj));
22+
// union of number with literal types should work fine
23+
expectType<boolean>(propEq(1, 'numLike', {} as Obj));
24+
expectType<boolean>(propEq('1', 'numLike', {} as Obj));
25+
// optional types doesn't fire an error, if passed correct types
26+
expectType<boolean>(propEq('str', 'optional', {} as Obj));
27+
expectType<boolean>(propEq(undefined, 'optional', {} as Obj));
28+
// fires an error only on wrong type
29+
expectError(propEq(1, 'optional', {} as Obj));
30+
expectError(propEq(null, 'optional', {} as Obj));
31+
// nullable types doesn't fire an error, if passed correct types
32+
expectType<boolean>(propEq('str', 'nullable', {} as Obj));
33+
expectType<boolean>(propEq(null, 'nullable', {} as Obj));
34+
// fires an error only on wrong type
35+
expectError(propEq(1, 'nullable', {} as Obj));
36+
expectError(propEq(undefined, 'nullable', {} as Obj));
37+
// unknown field names fails
38+
expectError(propEq('foo', 'unknown', {} as Obj));
39+
// should work with arrays as well
40+
expectType<boolean>(propEq(1, 0, [] as NumArr));
41+
// numeric array should expect only numbers
42+
expectError(propEq('foo', 0, [] as NumArr));
43+
// array can't accept string as prop name
44+
expectError(propEq(1, 'foo', [] as NumArr));
1945

46+
// ######################
2047
// propEq(val)(name)(obj)
2148
expectType<boolean>(propEq('foo')('union')({} as Obj));
2249
// 'nope' is inferred as 'string' here.
2350
expectType<boolean>(propEq('nope')('union')({} as Obj));
24-
// completely different type fails
25-
expectError(propEq(2)('union')({} as Obj));
51+
// union of number with literal types should work fine
52+
expectType<boolean>(propEq(1)('numLike')({} as Obj));
53+
expectType<boolean>(propEq('1')('numLike')({} as Obj));
54+
// optional types doesn't fire an error, if passed correct types
55+
expectType<boolean>(propEq('str')('optional')({} as Obj));
56+
expectType<boolean>(propEq(undefined)('optional')({} as Obj));
57+
// fires an error only on wrong type
58+
expectError(propEq(1)('optional')({} as Obj));
59+
expectError(propEq(null)('optional')({} as Obj));
60+
// nullable types doesn't fire an error, if passed correct types
61+
expectType<boolean>(propEq('str')('nullable')({} as Obj));
62+
expectType<boolean>(propEq(null)('nullable')({} as Obj));
63+
// fires an error only on wrong type
64+
expectError(propEq(1)('nullable')({} as Obj));
65+
expectError(propEq(undefined)('nullable')({} as Obj));
66+
// unknown field names fails
67+
expectError(propEq('foo')('unknown')({} as Obj));
2668

27-
// propEq(val)(name), obj)
69+
// ######################
70+
// propEq(val)(name, obj)
2871
expectType<boolean>(propEq('foo')('union', {} as Obj));
2972
// 'nope' is inferred as 'string' here.
3073
expectType<boolean>(propEq('nope')('union', {} as Obj));
31-
// completely different type fails
32-
expectError(propEq(2)('union', {} as Obj));
74+
// union of number with literal types should work fine
75+
expectType<boolean>(propEq(1)('numLike', {} as Obj));
76+
expectType<boolean>(propEq('1')('numLike', {} as Obj));
77+
// optional types doesn't fire an error, if passed correct types
78+
expectType<boolean>(propEq('str')('optional', {} as Obj));
79+
expectType<boolean>(propEq(undefined)('optional', {} as Obj));
80+
// fires an error only on wrong type
81+
expectError(propEq(1)('optional', {} as Obj));
82+
expectError(propEq(null)('optional', {} as Obj));
83+
// nullable types doesn't fire an error, if passed correct types
84+
expectType<boolean>(propEq('str')('nullable', {} as Obj));
85+
expectType<boolean>(propEq(null)('nullable', {} as Obj));
86+
// fires an error only on wrong type
87+
expectError(propEq(1)('nullable', {} as Obj));
88+
expectError(propEq(undefined)('nullable', {} as Obj));
89+
// unknown field names fails
90+
expectError(propEq('foo')('unknown', {} as Obj));
3391

92+
// ######################
3493
// propEq(val, name)(obj)
3594
expectType<boolean>(propEq('foo', 'union')({} as Obj));
3695
// 'nope' is inferred as 'string' here.
3796
expectType<boolean>(propEq('nope', 'union')({} as Obj));
38-
// completely different type fails
39-
expectError(propEq(2, 'union')({} as Obj));
97+
// union of number with literal types should work fine
98+
expectType<boolean>(propEq(1, 'numLike')({} as Obj));
99+
expectType<boolean>(propEq('1', 'numLike')({} as Obj));
100+
// optional types doesn't fire an error, if passed correct types
101+
expectType<boolean>(propEq('str', 'optional')({} as Obj));
102+
expectType<boolean>(propEq(undefined, 'optional')({} as Obj));
103+
// fires an error only on wrong type
104+
expectError(propEq(1, 'optional')({} as Obj));
105+
expectError(propEq(null, 'optional')({} as Obj));
106+
// nullable types doesn't fire an error, if passed correct types
107+
expectType<boolean>(propEq('str', 'nullable')({} as Obj));
108+
expectType<boolean>(propEq(null, 'nullable')({} as Obj));
109+
// fires an error only on wrong type
110+
expectError(propEq(1, 'nullable')({} as Obj));
111+
expectError(propEq(undefined, 'nullable')({} as Obj));
112+
// unknown field names fails
113+
expectError(propEq('foo', 'unknown')({} as Obj));
114+
115+
// ##########################
116+
// propEq(__, name, obj)(val)
117+
expectType<boolean>(propEq(__, 'union', {} as Obj)('foo'));
118+
// propEq(val, __, obj)(val)
119+
expectType<boolean>(propEq('foo', __, {} as Obj)('union'));
120+
// propEq(__, __, obj)(val, name)
121+
expectType<boolean>(propEq(__, __, {} as Obj)('foo', 'union'));
122+
// propEq(__, __, obj)(val)(name)
123+
expectType<boolean>(propEq(__, __, {} as Obj)('foo')('union'));
124+
125+
expectError(propEq('foo', __, {} as Obj)('unknown'));
126+
expectError(propEq(__, __, {} as Obj)('foo', 'unknown'));
127+
expectError(propEq(__, __, {} as Obj)('foo')('unknown'));

types/propEq.d.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
1-
export function propEq<T>(val: T): {
2-
<K extends PropertyKey>(name: K): (obj: Record<K, T>) => boolean;
3-
<K extends PropertyKey>(name: K, obj: Record<K, T>): boolean;
1+
import { Placeholder } from 'ramda';
2+
import { WidenLiterals } from '../util/tools';
3+
4+
export function propEq(__: Placeholder): never;
5+
export function propEq<V>(val: V): {
6+
<K extends PropertyKey>(name: K): K extends number
7+
? (array: Array<WidenLiterals<V>>) => boolean
8+
: (obj: Record<K, WidenLiterals<V>>) => boolean;
9+
<const U>(__: Placeholder, obj: U): (name: keyof U) => boolean;
10+
<K extends keyof U, const U>(name: K, obj: U): boolean;
411
};
5-
export function propEq<T, K extends PropertyKey>(val: T, name: K): (obj: Record<K, T>) => boolean;
6-
export function propEq<K extends keyof U, U>(val: U[K], name: K, obj: U): boolean;
12+
export function propEq<K extends PropertyKey>(__: Placeholder, name: K): K extends number ? {
13+
<U extends any[]>(__: Placeholder, array: U): (val: WidenLiterals<U[K]>) => boolean;
14+
<U extends any[]>(val: WidenLiterals<U[K]>, array: U): boolean
15+
<V>(val: V): (array: Array<WidenLiterals<V>>) => boolean
16+
} : {
17+
<U extends Record<K, any>>(__: Placeholder, obj: U): (val: WidenLiterals<U[K]>) => boolean;
18+
<U extends Record<K, any>>(val: WidenLiterals<U[K]>, obj: U): boolean
19+
<V>(val: V): (obj: Record<K, WidenLiterals<V>>) => boolean
20+
};
21+
export function propEq<V, K extends PropertyKey>(val: V, name: K): K extends number ?
22+
(array: Array<WidenLiterals<V>>) => boolean :
23+
(obj: Record<K, WidenLiterals<V>>) => boolean;
24+
25+
export function propEq<U>(__: Placeholder, ___: Placeholder, obj: U): {
26+
<K extends keyof U>(__: Placeholder, name: K): (val: WidenLiterals<U[K]>) => boolean
27+
<K extends keyof U>(val: WidenLiterals<U[K]>, name: K): boolean;
28+
<K extends keyof U>(val: WidenLiterals<U[K]>): (name: K) => boolean;
29+
};
30+
export function propEq<K extends keyof U, const U>(__: Placeholder, name: K, obj: U): (val: WidenLiterals<U[K]>) => boolean;
31+
export function propEq<K extends keyof U, const U>(val: WidenLiterals<U[K]>, __: Placeholder, obj: U): (name: K) => boolean;
32+
export function propEq<K extends keyof U, const U>(val: WidenLiterals<U[K]>, name: K, obj: U): boolean;

0 commit comments

Comments
 (0)