From 24183a00de452568584304473ad8c9cf77f7a356 Mon Sep 17 00:00:00 2001 From: Harris Miller Date: Wed, 21 Feb 2024 20:00:44 -0700 Subject: [PATCH] Revert "Update: propEq to allow wider-typing for value in comparison (#74)" This reverts commit ec0039073159a2968c81c07e6fed138d9f010d5b. --- test/allPass.test.ts | 18 ----- test/anyPass.test.ts | 29 +------ test/propEq.test.ts | 180 ++++++++----------------------------------- types/allPass.d.ts | 61 +-------------- types/anyPass.d.ts | 64 ++------------- types/propEq.d.ts | 13 +--- 6 files changed, 47 insertions(+), 318 deletions(-) diff --git a/test/allPass.test.ts b/test/allPass.test.ts index 1e3ee1cf..37040dd0 100644 --- a/test/allPass.test.ts +++ b/test/allPass.test.ts @@ -48,21 +48,3 @@ expectError( nickname: 'Blade' }) ); - -const isQueen = propEq('Q', 'rank'); -const isSpade = propEq('♠︎', 'suit'); -const isQueenOfSpades = allPass([isQueen, isSpade]); - -isQueenOfSpades({ - rank: '2', - suit: '♠︎' -}); - -const isQueen2 = (x: Record<'rank', string>) => x.rank === 'Q'; -const isSpade2 = (x: Record<'suit', string>) => x.suit === '♠︎'; -const isQueenOfSpades2 = allPass([isQueen2, isSpade2]); - -isQueenOfSpades2({ - rank: '2', - suit: '♠︎' -}); diff --git a/test/anyPass.test.ts b/test/anyPass.test.ts index e9923872..ff46aa58 100644 --- a/test/anyPass.test.ts +++ b/test/anyPass.test.ts @@ -25,13 +25,13 @@ expectType( }) ); -expectType( +expectError( isVampire({ age: 21, garlic_allergy: true, sun_allergy: true, - fast: null, - fear: undefined + fast: false, + fear: true }) ); @@ -48,26 +48,3 @@ expectError( nickname: 'Blade' }) ); - -const isQueen = propEq('Q', 'rank'); -const isSpade = propEq('♠︎', 'suit'); -const isQueenOfSpades = anyPass([isQueen, isSpade]); - -expectType(isQueenOfSpades({ - rank: '2', - suit: '♠︎' -})); - -expectError(isQueenOfSpades({ - rank: 2, - suit: '♠︎' -})); - -const isQueen2 = (x: Record<'rank', string>) => x.rank === 'Q'; -const isSpade2 = (x: Record<'suit', string>) => x.suit === '♠︎'; -const isQueenOfSpades2 = anyPass([isQueen2, isSpade2]); - -isQueenOfSpades2({ - rank: '2', - suit: '♠︎' -}); diff --git a/test/propEq.test.ts b/test/propEq.test.ts index 89d70b6a..dc5b9098 100644 --- a/test/propEq.test.ts +++ b/test/propEq.test.ts @@ -3,153 +3,37 @@ import { expectError, expectType } from 'tsd'; import { propEq } from '../es'; type Obj = { - literals: 'A' | 'B'; - unions: number | string; - nullable: number | null | undefined; - optional?: number; + union: 'foo' | 'bar'; + str: string; + num: number; + u: undefined; + n: null; }; -const obj = {} as Obj; - -// -// literals -// - -// happy path works as expected -expectType(propEq('A')('literals')(obj)); -expectType(propEq('A', 'literals')(obj)); -expectType(propEq('A', 'literals', obj)); - -// accepts any type that obj[key] can be widened too -expectType(propEq('C')('literals')(obj)); -expectType(propEq('C', 'literals')(obj)); -// only propEq(val, key, obj) requests non-widened types -expectError(propEq('C', 'literals', obj)); - -// rejects if type cannot be widened too -expectError(propEq(2)('literals')(obj)); -expectError(propEq(2, 'literals')(obj)); -expectError(propEq(2, 'literals', obj)); - -// manually widened also works -expectType(propEq('A' as string)('literals')(obj)); -expectType(propEq('A' as string, 'literals')(obj)); -// only rejects for propEq(val, key, obj), `string` is too wide for 'A' | 'B' -expectError(propEq('A' as string, 'literals', obj)); - -// rejects if key is not on obj -expectError(propEq('A')('literals')({} as Omit)); -expectError(propEq('A', 'literals')({} as Omit)); -expectError(propEq('A', 'literals', {} as Omit)); - -// rejects empty object literal -expectError(propEq('A')('literals')({})); -expectError(propEq('A', 'literals')({})); -expectError(propEq('A', 'literals', {})); - -// -// unions -// - -// happy path works as expected -expectType(propEq('1')('unions')(obj)); -expectType(propEq('1', 'unions')(obj)); -expectType(propEq('1', 'unions', obj)); - -expectType(propEq(1)('unions')(obj)); -expectType(propEq(1, 'unions')(obj)); -expectType(propEq(1, 'unions', obj)); - -// rejects if typeof val not part of union type -expectError(propEq(true)('unions')(obj)); -expectError(propEq(true, 'unions')(obj)); -expectError(propEq(true, 'unions', obj)); - -// rejects if key is not on obj -expectError(propEq('1')('unions')({} as Omit)); -expectError(propEq('1', 'unions')({} as Omit)); -expectError(propEq('1', 'unions', {} as Omit)); - -// rejects empty object literal -expectError(propEq('1')('unions')({})); -expectError(propEq('1', 'unions')({})); -expectError(propEq('1', 'unions', {})); - -// -// nullable -// - -// happy path works as expected -expectType(propEq(1)('nullable')(obj)); -expectType(propEq(1, 'nullable')(obj)); -expectType(propEq(1, 'nullable', obj)); - -expectType(propEq(null)('nullable')(obj)); -expectType(propEq(null, 'nullable')(obj)); -expectType(propEq(null, 'nullable', obj)); - -expectType(propEq(undefined)('nullable')(obj)); -expectType(propEq(undefined, 'nullable')(obj)); -expectType(propEq(undefined, 'nullable', obj)); - -// rejects if typeof val not part of union type -expectError(propEq(true)('nullable')(obj)); -expectError(propEq(true, 'nullable')(obj)); -expectError(propEq(true, 'nullable', obj)); - -// rejects if key is not on obj -expectError(propEq(1)('nullable')({} as Omit)); -expectError(propEq(1, 'nullable')({} as Omit)); -expectError(propEq(1, 'nullable', {} as Omit)); - -// rejects empty object literal -expectError(propEq(1)('nullable')({})); -expectError(propEq(1, 'nullable')({})); -expectError(propEq(1, 'nullable', {})); - -// -// optional -// - -// happy path works as expected -expectType(propEq(1)('optional')(obj)); -expectType(propEq(1, 'optional')(obj)); -expectType(propEq(1, 'optional', obj)); - -expectType(propEq(undefined)('optional')(obj)); -expectType(propEq(undefined, 'optional')(obj)); -expectType(propEq(undefined, 'optional', obj)); - -// `null` produces error for `optional`. this is expected because typescript strictNullCheck `null !== undefined` -expectError(propEq(null)('optional')(obj)); -expectError(propEq(null, 'optional')(obj)); -expectError(propEq(null, 'optional', obj)); - -// rejects if typeof val not part of union type -expectError(propEq(true)('optional')(obj)); -expectError(propEq(true, 'optional')(obj)); -expectError(propEq(true, 'optional', obj)); - -// rejects if key is not on obj -expectError(propEq(1)('optional')({} as Omit)); -expectError(propEq(1, 'optional')({} as Omit)); -expectError(propEq(1, 'optional', {} as Omit)); - -// rejects empty object literal literal -expectError(propEq(1)('optional')({})); -expectError(propEq(1, 'optional')({})); -expectError(propEq(1, 'optional', {})); - -// -// other non-happy paths -// - -// rejects unknown key -expectError(propEq(1)('whatever')(obj)); -expectError(propEq(1, 'whatever')(obj)); -expectError(propEq(1, 'whatever', obj)); - -// rejects unknown key on emptyu object literal -expectError(propEq(1)('whatever')({})); -expectError(propEq(1, 'whatever')({})); -expectError(propEq(1, 'whatever', {})); +// propEq(val, name, obj) +expectType(propEq('foo', 'union', {} as Obj)); +// non-union string fails +expectError(propEq('nope', 'union', {} as Obj)); +// completely different type fails +expectError(propEq(2, 'union', {} as Obj)); + +// propEq(val)(name)(obj) +expectType(propEq('foo')('union')({} as Obj)); +// 'nope' is inferred as 'string' here. +expectType(propEq('nope')('union')({} as Obj)); +// completely different type fails +expectError(propEq(2)('union')({} as Obj)); + +// propEq(val)(name), obj) +expectType(propEq('foo')('union', {} as Obj)); +// 'nope' is inferred as 'string' here. +expectType(propEq('nope')('union', {} as Obj)); +// completely different type fails +expectError(propEq(2)('union', {} as Obj)); + +// propEq(val, name)(obj) +expectType(propEq('foo', 'union')({} as Obj)); +// 'nope' is inferred as 'string' here. +expectType(propEq('nope', 'union')({} as Obj)); +// completely different type fails +expectError(propEq(2, 'union')({} as Obj)); diff --git a/types/allPass.d.ts b/types/allPass.d.ts index f3a0cc6a..d4027384 100644 --- a/types/allPass.d.ts +++ b/types/allPass.d.ts @@ -1,24 +1,11 @@ -// narrowing export function allPass( - predicates: [ - (a: T) => a is TF1, - (a: T) => a is TF2 - ] + predicates: [(a: T) => a is TF1, (a: T) => a is TF2] ): (a: T) => a is TF1 & TF2; export function allPass( - predicates: [ - (a: T) => a is TF1, - (a: T) => a is TF2, - (a: T) => a is TF3 - ], + predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3], ): (a: T) => a is TF1 & TF2 & TF3; export function allPass( - predicates: [ - (a: T) => a is TF1, - (a: T) => a is TF2, - (a: T) => a is TF3, - (a: T) => a is TF4 - ], + predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3, (a: T) => a is TF4], ): (a: T) => a is TF1 & TF2 & TF3 & TF4; export function allPass( predicates: [ @@ -39,46 +26,4 @@ export function allPass a is TF6 ], ): (a: T) => a is TF1 & TF2 & TF3 & TF4 & TF5 & TF6; -// regular -export function allPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean - ], -): (a: T1 & T2) => boolean; -export function allPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean, - (a: T3) => boolean - ], -): (a: T1 & T2 & T3) => boolean; -export function allPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean, - (a: T3) => boolean, - (a: T4) => boolean - ], -): (a: T1 & T2 & T3 & T4) => boolean; -export function allPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean, - (a: T3) => boolean, - (a: T4) => boolean, - (a: T5) => boolean - ], -): (a: T1 & T2 & T3 & T4 & T5) => boolean; -export function allPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean, - (a: T3) => boolean, - (a: T4) => boolean, - (a: T5) => boolean, - (a: T6) => boolean - ], -): (a: T1 & T2 & T3 & T4 & T5 & T6) => boolean; -// catch-all export function allPass boolean>(predicates: readonly F[]): F; diff --git a/types/anyPass.d.ts b/types/anyPass.d.ts index 48ca9664..a056d84c 100644 --- a/types/anyPass.d.ts +++ b/types/anyPass.d.ts @@ -1,24 +1,14 @@ -// narrowing export function anyPass( - predicates: [ - (a: T) => a is TF1, - (a: T) => a is TF2 - ], + predicates: [(a: T) => a is TF1, (a: T) => a is TF2], ): (a: T) => a is TF1 | TF2; export function anyPass( - predicates: [ - (a: T) => a is TF1, - (a: T) => a is TF2, - (a: T) => a is TF3 - ], + predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3], +): (a: T) => a is TF1 | TF2 | TF3; +export function anyPass( + predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3], ): (a: T) => a is TF1 | TF2 | TF3; export function anyPass( - predicates: [ - (a: T) => a is TF1, - (a: T) => a is TF2, - (a: T) => a is TF3, - (a: T) => a is TF4 - ], + predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3, (a: T) => a is TF4], ): (a: T) => a is TF1 | TF2 | TF3 | TF4; export function anyPass( predicates: [ @@ -39,46 +29,4 @@ export function anyPass a is TF6 ], ): (a: T) => a is TF1 | TF2 | TF3 | TF4 | TF5 | TF6; -// regular -export function anyPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean - ], -): (a: T1 & T2) => boolean; -export function anyPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean, - (a: T3) => boolean - ], -): (a: T1 & T2 & T3) => boolean; -export function anyPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean, - (a: T3) => boolean, - (a: T4) => boolean - ], -): (a: T1 & T2 & T3 & T4) => boolean; -export function anyPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean, - (a: T3) => boolean, - (a: T4) => boolean, - (a: T5) => boolean - ], -): (a: T1 & T2 & T3 & T4 & T5) => boolean; -export function anyPass( - predicates: [ - (a: T1) => boolean, - (a: T2) => boolean, - (a: T3) => boolean, - (a: T4) => boolean, - (a: T5) => boolean, - (a: T6) => boolean - ], -): (a: T1 & T2 & T3 & T4 & T5 & T6) => boolean; -// catch-all export function anyPass boolean>(predicates: readonly F[]): F; diff --git a/types/propEq.d.ts b/types/propEq.d.ts index d4f4a7de..e667fb28 100644 --- a/types/propEq.d.ts +++ b/types/propEq.d.ts @@ -1,13 +1,6 @@ -import { WidenLiterals } from './util/tools'; - -// propEq(val) export function propEq(val: T): { - // propEq(val)(name)(obj) - (name: K): >>(obj: Required extends Record ? T extends WidenLiterals ? U : never : never) => boolean; - // propEq(val)(name, obj) - >>(name: K, obj: Required extends Record ? T extends WidenLiterals ? U : never : never): boolean; + (name: K): (obj: Record) => boolean; + (name: K, obj: Record): boolean; }; -// propEq(val, name)(obj) -export function propEq(val: T, name: K): >>(obj: Required extends Record ? T extends WidenLiterals ? U : never : never) => boolean; -// propEq(val, name, obj) +export function propEq(val: T, name: K): (obj: Record) => boolean; export function propEq(val: U[K], name: K, obj: U): boolean;