Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nested object methods #81

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/object/at-path-n.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { $, Test, Object } from '..'

/**
* Tests for `Object.AtPath`, which returns the value at a given path in an
* object. The path is specified as a tuple of keys.
*/
type AtPath_NSpec = [
/**
* Can get the value at a key and path.
*/
Test.Expect<
$<
$<Object.AtPathN, ['age', ['name', 'first']]>,
{
name: {
first: 'foo'
last: string
}
age: 30
}
>,
'foo' | 30
>,

/**
* Can get the value at a path and key in a union.
*/
Test.Expect<
$<
$<Object.AtPathN, ['age', ['name', 'first']]>,
| {
name: {
first: 'foo'
last: string
}
age: 20
}
| {
name: {
first: 'bar'
last: string
}
age: 30
}
>,
'foo' | 'bar' | 20 | 30
>,

/**
* Will emit never if the path does not exist.
*/
Test.Expect<
$<
$<Object.AtPathN, [['name', 'first']]>,
{ name: { last: string }; age: number }
>,
never
>,
/**
* Emits an error if applied to a non-object.
*/
// @ts-expect-error
$<$<Object.AtPathN, [['name', 'first']]>, number>
]
35 changes: 35 additions & 0 deletions src/object/at-path-n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Kind, Type } from '..'
import { _$atPath } from './at-path'

export type KeyOrPath = PropertyKey | PropertyKey[]

export type _$atPathN<
Path extends KeyOrPath[],
T,
Acc extends any[] = [],
Output = Path extends [
infer Head extends KeyOrPath,
...infer Tail extends KeyOrPath[]
]
? _$atPathN<
Tail,
T,
[
...Acc,
Head extends PropertyKey[]
? _$atPath<Head, T>
: T[Type._$cast<Head, keyof T>]
]
>
: Acc[number]
> = Output

interface AtPathN_T<Path extends KeyOrPath[]> extends Kind.Kind {
f(
x: Type._$cast<this[Kind._], Record<string, unknown>>
): _$atPathN<Path, typeof x>
}

export interface AtPathN extends Kind.Kind {
f(x: Type._$cast<this[Kind._], KeyOrPath[]>): AtPathN_T<typeof x>
}
11 changes: 4 additions & 7 deletions src/object/at-path.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import { Kind, Type } from '..'

export type _$atPath<Path extends (string | symbol)[], T> = Path extends [
export type _$atPath<Path extends PropertyKey[], T> = Path extends [
infer Head,
...infer Tail
]
? Tail extends []
? Head extends keyof T
? T[Head]
: never
: _$atPath<
Type._$cast<Tail, (string | symbol)[]>,
T[Type._$cast<Head, keyof T>]
>
: _$atPath<Type._$cast<Tail, PropertyKey[]>, T[Type._$cast<Head, keyof T>]>
: never

interface AtPath_T<Path extends (string | symbol)[]> extends Kind.Kind {
interface AtPath_T<Path extends PropertyKey[]> extends Kind.Kind {
f(
x: Type._$cast<this[Kind._], Record<string, unknown>>
): _$atPath<Path, typeof x>
}

export interface AtPath extends Kind.Kind {
f(x: Type._$cast<this[Kind._], (string | symbol)[]>): AtPath_T<typeof x>
f(x: Type._$cast<this[Kind._], PropertyKey[]>): AtPath_T<typeof x>
}
3 changes: 3 additions & 0 deletions src/object/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './at-path'
export * from './at-path-n'
export * from './at'
export * from './deep-input-of'
export * from './deep-map-values'
Expand All @@ -9,3 +10,5 @@ export * from './map-values'
export * from './merge'
export * from './paths'
export * from './values'
export * from './update'
export * from './update-n'
113 changes: 113 additions & 0 deletions src/object/update-n.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { $, Test, Object } from '..'

type UpdateN_Spec = [
/**
* Can Update List Of Keys
*/
Test.Expect<
$<
$<$<Object.UpdateN, ['name', 'age']>, ['x', 20]>,
{
name: {
first: 'Joe'
}
age: number
}
>,
{
name: 'x'
age: 20
}
>,
/**
* Can Update nested paths
*/
Test.Expect<
$<
$<
$<Object.UpdateN, [['name', 'first'], ['location', 'country']]>,
['David', { state: 'California'; city: 'Sacramento' }]
>,
{
name: {
first: 'Joe'
}
location: {
country: {
city: 'Michigan'
}
}
}
>,
{
name: {
first: 'David'
}
location: {
country: { state: 'California'; city: 'Sacramento' }
}
}
>,
/**
* Can Update a list of keys and nested paths
*/
Test.Expect<
$<
$<
$<
Object.UpdateN,
['age', ['name', 'first'], ['location', 'country', 'city']]
>,
[30, 'David', 'SF']
>,
{
name: {
first: 'Joe'
}
age: number
location: {
country: {
city: 'Michigan'
}
}
}
>,
{
name: {
first: 'David'
}
age: 30
location: {
country: {
city: 'SF'
}
}
}
>,

/**
* Keeps original object if the path is invalid
*/
Test.Expect<
$<
$<$<Object.UpdateN, [['name', 'last']]>, ['bar']>,
{
name: {
first: 'Joe'
}
age: number
}
>,
{
name: {
first: 'Joe'
}
age: number
}
>,
/**
* Running 'Update' on a non-Object type should emit an error.
*/
//@ts-expect-error
$<$<$<Object.Update, ['name', 'first', 'last']>, 'bar'>, number>
]
43 changes: 43 additions & 0 deletions src/object/update-n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Kind, Type } from '..'
import { KeyOrPath } from './at-path-n'
import { _$update } from './update'

export type _$updateN<
P extends KeyOrPath[],
V extends unknown[],
O extends Record<string, unknown>
> = [[P], [V]] extends [
[[infer Head, ...infer Tail extends KeyOrPath[]]],
[[infer VHead, ...infer VTail]]
]
? _$updateN<
Tail,
VTail,
Type._$cast<
_$update<
Type._$cast<
Head extends PropertyKey[] ? Head : [Head],
PropertyKey[]
>,
VHead,
O
>,
Record<PropertyKey, unknown>
>
>
: O

interface UpdateN_T2<PATHS extends KeyOrPath[], VALUE extends unknown[]>
extends Kind.Kind {
f(
x: Type._$cast<this[Kind._], Record<PropertyKey, unknown>>
): _$updateN<PATHS, VALUE, typeof x>
}

interface UpdateN_T<PATHS extends KeyOrPath[]> extends Kind.Kind {
f(x: Type._$cast<this[Kind._], unknown[]>): UpdateN_T2<PATHS, typeof x>
}

export interface UpdateN extends Kind.Kind {
f(x: Type._$cast<this[Kind._], KeyOrPath[]>): UpdateN_T<typeof x>
}
65 changes: 65 additions & 0 deletions src/object/update.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { $, Test, Object } from '..'

type Update_Spec = [
/**
* Can Update at key.
*/
Test.Expect<
$<
$<$<Object.Update, ['name']>, { fullname: 'Joe Doe' }>,
{
name: {
first: 'Joe'
}
age: number
}
>,
{
name: { fullname: 'Joe Doe' }
age: number
}
>,
/**
* Can Update at nestd path.
*/
Test.Expect<
$<
$<$<Object.Update, ['name', 'first']>, 'Jane'>,
{
name: {
first: 'Joe'
}
age: number
}
>,
{
name: { first: 'Jane' }
age: number
}
>,
/**
* Keeps original object if the path is invalid
*/
Test.Expect<
$<
$<$<Object.Update, ['name', 'first', 'last']>, 'bar'>,
{
name: {
first: 'Joe'
}
age: number
}
>,
{
name: {
first: 'Joe'
}
age: number
}
>,
/**
* Running 'Update' on a non-Object type should emit an error.
*/
//@ts-expect-error
$<$<$<Object.Update, ['name', 'first', 'last']>, 'bar'>, number>
]
Loading
Loading