Skip to content

Commit

Permalink
Fixed some typings
Browse files Browse the repository at this point in the history
  • Loading branch information
mweststrate committed Dec 28, 2018
1 parent 578b3bd commit ecc7760
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 39 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,13 @@ Todo:
* [x] implement `SubscribeOptions`
* [x] keepAlive drv option, using effect
* [x] publish all script
* [x] tests and types for utils
* [ ] kill `run`
* [ ] rval-validation
* [ ] verify callign actions in reactions work correctly
* [ ] contributing and debugging
* [ ] docs
* [ ] verify debugging with minification
* [ ] tests and types for utils
* [ ] move `invariant` to preprocessors?
* [ ] add `reference` to models?
* [ ] contributing & debugging guide. `reserved` section in package.json!
Expand Down
2 changes: 2 additions & 0 deletions docs_source/1_advanced/organizing_state.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ route: /advanced/state-design

# Designing the state shape

## Trees, everywhere

## Choosing immutability granularity level

## Composition, normalization and association
Expand Down
6 changes: 3 additions & 3 deletions pkgs/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ class ObservableValue<T> implements ObservableAdministration {
}
}
freeze(v) {
if (this.context.config.autoFreeze && (Array.isArray(v) || isPlainObject(v))) _deepfreeze(v)
if (this.context.config.autoFreeze && (Array.isArray(v) || _isPlainObject(v))) _deepfreeze(v)
return v
}
}
Expand Down Expand Up @@ -443,8 +443,8 @@ function hiddenProp(target, key, value) {
})
}

function isPlainObject(o) {
const p = Object.getPrototypeOf(o)
export function _isPlainObject(o) {
const p = o && typeof o === "object" && Object.getPrototypeOf(o)
return p === Object.prototype || p === null
}

Expand Down
2 changes: 1 addition & 1 deletion pkgs/core/tests/scheduling.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { val, sub, drv, batch, effect, $RVal } from '@r-val/core'
import { val, sub, drv, effect, $RVal } from '@r-val/core'

function getDeps(thing) {
return Array.from(thing[$RVal].listeners)
Expand Down
2 changes: 1 addition & 1 deletion pkgs/updaters/tests/updaters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ test("assign", () => {
}

expect(assign({})(base)).toBe(base)
expect(assign(null)(base)).toBe(base)
expect(assign(null as any)(base)).toBe(base)
expect(assign({x: 1, y: 2})(base)).toBe(base)
expect(assign({x: 2, y: 2})(base)).not.toBe(base)
expect(assign({x: 2, y: 2})(base)).toEqual({ x: 2, y: 2 })
Expand Down
13 changes: 6 additions & 7 deletions pkgs/updaters/updaters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,18 @@ export function pop<T>(val: T[]): T[] {
return res
}

export const assign = <T>(v:T) => (o: T) => {
export const assign = <A, B>(v:A) => (o: B) => {
if (!v) return o
let change = false
for (const key in v) if (o[key] !== v[key]) {
for (const key in v) if ((o as any) [key] !== v[key]) {
change = true
break
}
return (change ? Object.assign({}, o, v) : o) as T
return (change ? Object.assign({}, o, v) : o) as (A & B)
}

export function removeBy<T>(predicate: (val: T) => boolean): (o: T[]) => T[]
export function removeBy<V, T extends KVMap<V>>(predicate: (val: V) => boolean): (o: T) => T
export function removeBy<T, K extends keyof T>(key: K, value: T[K]): (o: T[]) => T[]
export function removeBy<V, T extends KVMap<V>, K extends keyof V>(key: K, value: V[K]): (o: T) => T
export function removeBy(key: string, value: any): <B>(o: B) => B
export function removeBy(predicate: (val: any) => boolean): <B>(o: B) => B
export function removeBy(arg1, arg2?) {
if (typeof arg1 !== "function")
return removeBy(v => v[arg1] === arg2)
Expand All @@ -118,6 +116,7 @@ export function removeBy(arg1, arg2?) {
}
}

export function removeValue(value: any): <B>(o: B) => B
export function removeValue(value) {
return removeBy(v => v === value)
}
Expand Down
77 changes: 77 additions & 0 deletions pkgs/utils/tests/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { val, drv, sub, act } from "@r-val/core"
import {toJS, assignVals, keepAlive} from "@r-val/utils"
import { delay } from "q";

test("toJS", () => {
expect(toJS(val(3))).toBe(3)

expect(toJS(drv(() => 3))).toBe(3)

expect(toJS(val({x: 3 }))).toEqual({x: 3})

expect(toJS(val({x: 3, toJS() { return 2 } }))).toEqual(2)

expect(toJS(val([3]))).toEqual([3])

expect(toJS(val({x: val(3) }))).toEqual({x: 3})

{
const x = {x: 3}
expect(toJS(x)).not.toBe(x)
}
})

test("assignVals", () => {
const x = {
a: val(3),
b: 2,
c: drv(() => x.b * 2, v => { x.b = v}),
}

assignVals(x, {
a: 2,
}, {
a: 4,
c: 5
})

expect(toJS(x)).toEqual({
a: 4,
b: 5,
c: 10
})
})

test("keepAlive", async () => {
let calc = 0
const x = val(1)
const y = drv(() => {
calc++
return x()
})

await delay(10)
const d = keepAlive(y)

expect(calc).toBe(0)
expect(y()).toBe(1)
expect(calc).toBe(1)

x(2)
await delay(10)
x(3)
expect(calc).toBe(1)
expect(y()).toBe(3)
expect(calc).toBe(2)
expect(y()).toBe(3)
expect(calc).toBe(2)

d()
await delay(10)
expect(y()).toBe(3)
expect(calc).toBe(3)

await delay(10)
expect(y()).toBe(3)
expect(calc).toBe(4)
})
47 changes: 21 additions & 26 deletions pkgs/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,40 @@
import { isVal, run, isDrv, Drv, Disposer, rval, _once } from "@r-val/core";

// type SnapshotType<T> = {
// [K in keyof T]?: T[K] extends Val<infer X, infer S>
// ? X | S
// : T[K] extends Drv<any>
// ? never
// : T[K] extends Function
// ? never
// : T[K] extends (string | number | boolean)
// ? T[K]
// : SnapshotType<T[K]>
// }
import { isVal, run, isDrv, Drv, Disposer, rval, _once, _isPlainObject, Val } from '@r-val/core'

// TODO: add typings!
export function toJS(thing) {
if (!thing) return thing;
if (typeof thing.toJS === "function") return thing.toJS()
if (!thing) return thing
if (typeof thing.toJS === 'function') return thing.toJS()
if (isVal(thing) || isDrv(thing)) return toJS(thing())
if (Array.isArray(thing)) return thing.map(toJS)
if (typeof thing === "object" && (Object.getPrototypeOf(thing) === Object || Object.getPrototypeOf(thing) === null)) {
if (_isPlainObject(thing)) {
const res = {}
for(const key in thing) res[key] = toJS(thing[key])
for (const key in thing) res[key] = toJS(thing[key])
return res
}
return thing
}

type AssignVals<T> = {
[K in keyof T]?: T[K] extends Val<infer T, infer S> ? T | S : T[K] extends Drv<infer T> ? T : never
}

export function assignVals<T>(target: T, vals: AssignVals<T>, ...moreVals: AssignVals<T>[])
export function assignVals(target, vals, ...moreVals) {
if (moreVals.length)
vals = Object.assign(vals, ...moreVals)
if (moreVals.length) vals = Object.assign(vals, ...moreVals)
run(() => {
for (const key in vals) {
if (isVal(target[key]) || isDrv(target[key]))
target[key](vals[key])
throw new Error(`[assignVals] value at key "${key}" is not a 'val' or 'drv'`)
if (isVal(target[key]) || isDrv(target[key])) target[key](vals[key])
else throw new Error(`[assignVals] value at key "${key}" is not a 'val' or 'drv'`)
}
})
return target
}

export function keepAlive(target: Drv<any>): Disposer {
return rval(target).effect(target, _once((didChange, pull) => {
didChange() // we never have to pull, we only detect for changes once, so that the target becomes hot
}))
}
return rval(target).effect(
target,
_once((didChange, pull) => {
didChange() // we never have to pull, we only detect for changes once, so that the target becomes hot
})
)
}

0 comments on commit ecc7760

Please sign in to comment.