diff --git a/packages/focal/src/utils.ts b/packages/focal/src/utils.ts index 81f27ae..d8010b4 100644 --- a/packages/focal/src/utils.ts +++ b/packages/focal/src/utils.ts @@ -4,17 +4,16 @@ export { equals as structEq } from './equals' export const DEV_ENV = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production' -export function setKey(k: K, v: T[K], o: T): T { +export function setKey(k: K, v: T[K], o: T): T { if (k in o && structEq(v, o[k])) { return o } else { - // this is the fastest way to do it, see - // https://jsperf.com/focal-setkey-for-loop-vs-object-assign - const r: { [k in keyof T]: T[k] } = {} as any - for (const p in o) r[p] = o[p] - r[k] = v - - return r + // Faster to use Object.assign and friends in modern JS engines. + // Object.create needed to preserve the instance's prototype. + const copy: T = Object.create(o) + Object.assign(copy, o) + copy[k] = v + return copy } } diff --git a/packages/focal/test/lens.test.ts b/packages/focal/test/lens.test.ts index 1a9d71f..45b1404 100644 --- a/packages/focal/test/lens.test.ts +++ b/packages/focal/test/lens.test.ts @@ -202,3 +202,26 @@ describe('property expressions', () => { process.env.NODE_ENV = originalNodeEnv }) }) +describe('object', () => { + class TestObject { + constructor(public num: number) { + } + getNum(): number { + return this.num + } + } + let o1 = new TestObject(0) + const l1 = Lens.key()('num') + + it('num lens', () => expect(l1.get(o1)).toEqual(0)) + it('num property', () => expect(o1.num).toEqual(0)) + + describe('after lens set', () => { + beforeEach(() => { + o1 = l1.set(10, o1) + }) + it('num lens', () => expect(l1.get(o1)).toEqual(10)) + it('num property', () => expect(o1.num).toEqual(10)) + it('getNum method', () => expect(o1.getNum()).toEqual(10)) + }) +})