-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Lexus Drumgold <[email protected]>
- Loading branch information
1 parent
7e8097d
commit b65d7ed
Showing
5 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
/** | ||
* @file Type Tests - HasKey | ||
* @module tutils/types/tests/unit-d/HasKey | ||
*/ | ||
|
||
import type Person from '#fixtures/interfaces/person' | ||
import type Vehicle from '#fixtures/types/vehicle' | ||
import type Fn from '../fn' | ||
import type TestSubject from '../has-key' | ||
import type Indices from '../indices' | ||
import type Nullable from '../nullable' | ||
import type { tag as opaque } from '../opaque' | ||
import type PropertyKey from '../property-key' | ||
import type Stringify from '../stringify' | ||
|
||
describe('unit-d:types/HasKey', () => { | ||
it('should equal false if K is never', () => { | ||
expectTypeOf<TestSubject<Vehicle, never>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal false if T is never', () => { | ||
expectTypeOf<TestSubject<never, 'vin'>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
describe('IsAny<T> extends true', () => { | ||
type T = any | ||
|
||
describe('K extends number', () => { | ||
it('should equal false if number does not extend K', () => { | ||
expectTypeOf<TestSubject<T, 1>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if number extends K', () => { | ||
expectTypeOf<TestSubject<T, number>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('K extends string', () => { | ||
it('should equal false if string does not extend K', () => { | ||
expectTypeOf<TestSubject<T, keyof Vehicle>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if string extends K', () => { | ||
expectTypeOf<TestSubject<T, string>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('K extends symbol', () => { | ||
it('should equal false if symbol does not extend K', () => { | ||
expectTypeOf<TestSubject<T, typeof opaque>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if symbol extends K', () => { | ||
expectTypeOf<TestSubject<T, symbol>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('T extends ObjectCurly', () => { | ||
type T = Vehicle | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
expectTypeOf<TestSubject<T, 'vrm'>>().toEqualTypeOf<false>() | ||
expectTypeOf<TestSubject<T, PropertyKey>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('T extends Primitive', () => { | ||
describe('T extends NIL', () => { | ||
it('should equal false if keyof T is never', () => { | ||
expectTypeOf<TestSubject<null, 'make'>>().toEqualTypeOf<false>() | ||
expectTypeOf<TestSubject<undefined, 'vin'>>().toEqualTypeOf<false>() | ||
}) | ||
}) | ||
|
||
describe('T extends bigint', () => { | ||
type T = 0n | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
expectTypeOf<TestSubject<T, PropertyKey>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('T extends boolean', () => { | ||
type T = false | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
expectTypeOf<TestSubject<T, PropertyKey>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('T extends number', () => { | ||
type T = 0 | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
expectTypeOf<TestSubject<T, PropertyKey>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('T extends string', () => { | ||
describe('IsLiteral<T> extends true', () => { | ||
type T = 'xyz' | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
// Arrange | ||
type K = Stringify<Indices<T>> | '-1' | '-2' | '-3' | ||
|
||
// Expect | ||
expectTypeOf<TestSubject<T, K>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
|
||
it('should equal true if K extends Indices<T>', () => { | ||
expectTypeOf<TestSubject<T, Indices<T>>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('number extends Indices<T>', () => { | ||
type T = string | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
expectTypeOf<TestSubject<T, string | symbol>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('T extends symbol', () => { | ||
type T = typeof opaque | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
expectTypeOf<TestSubject<T, PropertyKey>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('T extends Readonly<Fn>', () => { | ||
type T = Readonly<Fn> | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
expectTypeOf<TestSubject<T, PropertyKey>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('T extends readonly unknown[]', () => { | ||
describe('IsTuple<T> extends true', () => { | ||
type T = readonly [Vehicle, Nullable<Vehicle>, Vehicle?] | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
// Arrange | ||
type K = '-1' | '-2' | '-3' | ||
|
||
// Expect | ||
expectTypeOf<TestSubject<T, K>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
|
||
it('should equal true if K extends Indices<T>', () => { | ||
expectTypeOf<TestSubject<T, Indices<T>>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
|
||
describe('number extends Indices<T>', () => { | ||
type T = Vehicle[] | ||
|
||
it('should equal false if K and keyof T do not intersect', () => { | ||
expectTypeOf<TestSubject<T, string | symbol>>().toEqualTypeOf<false>() | ||
}) | ||
|
||
it('should equal true if K and keyof T intersect', () => { | ||
expectTypeOf<TestSubject<T, keyof T>>().toEqualTypeOf<true>() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('unions', () => { | ||
it('should distribute over unions', () => { | ||
// Arrange | ||
type T = Person | Vehicle | ||
|
||
// Expect | ||
expectTypeOf<TestSubject<T, 'name' | 'vin'>>().toEqualTypeOf<boolean>() | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* @file Type Tests - IfKey | ||
* @module tutils/types/tests/unit-d/IfKey | ||
*/ | ||
|
||
import type Vehicle from '#fixtures/types/vehicle' | ||
import type TestSubject from '../if-key' | ||
import type Nilable from '../nilable' | ||
|
||
describe('unit-d:types/IfKey', () => { | ||
type F = 0 | ||
type T = 1 | ||
|
||
it('should equal F if HasKey<U, K> extends false', () => { | ||
expectTypeOf<TestSubject<unknown, 'vin', T, F>>().toEqualTypeOf<F>() | ||
}) | ||
|
||
it('should equal F if U is never', () => { | ||
expectTypeOf<TestSubject<never, 'vrm', T, F>>().toEqualTypeOf<F>() | ||
}) | ||
|
||
it('should equal T if HasKey<U, K> extends true', () => { | ||
expectTypeOf<TestSubject<Vehicle, 'vin', T, F>>().toEqualTypeOf<T>() | ||
}) | ||
|
||
describe('unions', () => { | ||
it('should distribute over unions', () => { | ||
// Arrange | ||
type U = Nilable<Vehicle> | ||
type K = keyof Vehicle | ||
|
||
// Expect | ||
expectTypeOf<TestSubject<U, K, T, F>>().toEqualTypeOf<F | T>() | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/** | ||
* @file Type Definitions - HasKey | ||
* @module tutils/types/HasKey | ||
*/ | ||
|
||
import type IfNegativeInteger from './if-integer-negative' | ||
import type IfNever from './if-never' | ||
import type Indices from './indices' | ||
import type Intersection from './intersection' | ||
import type Remap from './remap' | ||
import type Stringify from './stringify' | ||
|
||
/** | ||
* Returns a boolean indicating if `K` and `keyof T` intersect. | ||
* | ||
* **Note**: If `K` and `keyof T` intersect, `K` can be used to index `T`. | ||
* | ||
* @todo examples | ||
* | ||
* @template T - Type to evaluate | ||
* @template K - Keys to evaluate | ||
*/ | ||
type HasKey<T, K> = IfNever< | ||
T, | ||
false, | ||
IfNever< | ||
K, | ||
false, | ||
T extends unknown | ||
? K extends keyof T | ||
? IfNever< | ||
keyof { | ||
[H in keyof Remap<T> as Intersection< | ||
T extends readonly unknown[] | ||
? Indices<T> extends infer I extends number | ||
? number extends I | ||
? H | ||
: H extends I | ||
? H | IfNegativeInteger<H, never, Stringify<H>> | ||
: H | ||
: never | ||
: H, | ||
K | ||
>]: H | ||
}, | ||
false, | ||
true | ||
> | ||
: false | ||
: never | ||
> | ||
> | ||
|
||
export type { HasKey as default } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/** | ||
* @file Type Definitions - IfKey | ||
* @module tutils/types/IfKey | ||
*/ | ||
|
||
import type HasKey from './has-key' | ||
import type IfNever from './if-never' | ||
|
||
/** | ||
* Returns a type that indicates if `K` and `keyof T` intersect. | ||
* | ||
* **Note**: If `K` and `keyof T` intersect, `K` can be used to index `T`. | ||
* | ||
* @see {@linkcode HasKey} | ||
* | ||
* @todo examples | ||
* | ||
* @template U - Type to evaluate | ||
* @template K - Keys to evaluate | ||
* @template T - Type if `K` and `keyof T` intersect | ||
* @template F - Type if `K` and `keyof T` do not intersect | ||
*/ | ||
type IfKey<U, K, T, F> = IfNever< | ||
U, | ||
F, | ||
U extends unknown | ||
? K extends unknown | ||
? HasKey<U, K> extends true | ||
? T | ||
: F | ||
: F | ||
: F | ||
> | ||
|
||
export type { IfKey as default } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters