diff --git a/tests/object/crush.test-d.ts b/tests/object/crush.test-d.ts new file mode 100644 index 00000000..059db22c --- /dev/null +++ b/tests/object/crush.test-d.ts @@ -0,0 +1,53 @@ +import * as _ from 'radashi' + +describe('crush', () => { + test('direct properties are preserved in the result type', () => { + const obj = _.crush({ a: 1, b: '2', c: true }) + expectTypeOf(obj).toEqualTypeOf<{ a: number; b: string; c: boolean }>() + }) + test('nested objects add Record to result type', () => { + const obj = _.crush({ a: { b: 1 } }) + expectTypeOf(obj).toEqualTypeOf>() + + const obj2 = _.crush({ a: 1, b: { c: 2 } }) + expectTypeOf(obj2).toEqualTypeOf<{ a: number; [key: string]: unknown }>() + expectTypeOf(obj2.a).toEqualTypeOf() + expectTypeOf(obj2['b.c']).toEqualTypeOf() + }) + test('optional property', () => { + const obj = _.crush({} as { a?: number }) + // FIXME: Try to preserve optionality. + expectTypeOf(obj).toEqualTypeOf<{ a: number | undefined }>() + }) + test('crushed Record type', () => { + const obj = _.crush({} as Record) + expectTypeOf(obj).toEqualTypeOf>() + + const obj2 = _.crush({} as Record) + expectTypeOf(obj2).toEqualTypeOf>() + + const obj3 = _.crush({} as Record) + expectTypeOf(obj3).toEqualTypeOf>() + + const obj4 = _.crush({} as Record) + expectTypeOf(obj4).toEqualTypeOf>() + }) + test('crushed array', () => { + // We cannot assume the keys from "number[]" input value, but we + // *can* assume the value type. Note that the value type doesn't + // contain "undefined" (which matches array behavior). + const obj = _.crush([1, 2, 3]) + expectTypeOf(obj).toEqualTypeOf>() + + const obj2 = _.crush([1, { b: 2 }]) + expectTypeOf(obj2).toEqualTypeOf>() + expectTypeOf(obj2[0]).toEqualTypeOf() + }) + test('union type with object and primitive', () => { + // Since "a" may be an object, we cannot assume the result will + // have an "a" property. Therefore, the keys and values of the + // result are unknown. + const obj = _.crush({ a: {} as number | object }) + expectTypeOf(obj).toEqualTypeOf>() + }) +}) diff --git a/tests/object/crush.test.ts b/tests/object/crush.test.ts index 16cfd0af..3645a8d6 100644 --- a/tests/object/crush.test.ts +++ b/tests/object/crush.test.ts @@ -33,30 +33,6 @@ describe('crush', () => { timestamp: now, }) }) - test('handles property names with dots 1', () => { - const obj = { - a: { 'b.c': 'value' }, - } - expect(_.crush(obj)).toEqual({ - 'a.b.c': 'value', - }) - }) - test('handles property names with dots 2', () => { - const obj = { - 'a.b': { c: 'value' }, - } - expect(_.crush(obj)).toEqual({ - 'a.b.c': 'value', - }) - }) - test('handles property names with dots 3', () => { - const obj = { - 'a.b': { 'c.d': 123.4 }, - } - expect(_.crush(obj)).toEqual({ - 'a.b.c.d': 123.4, - }) - }) test('handles arrays', () => { const obj = ['value', 123.4, true] expect(_.crush(obj)).toEqual({ @@ -65,4 +41,59 @@ describe('crush', () => { '2': true, }) }) + describe('property names containing period', () => { + test('inside the root object', () => { + const obj = { + 'a.b': { c: 'value' }, + } + expect(_.crush(obj)).toEqual({ + 'a.b.c': 'value', + }) + }) + test('inside a nested object', () => { + const obj = { + a: { 'b.c': 'value' }, + } + expect(_.crush(obj)).toEqual({ + 'a.b.c': 'value', + }) + }) + test('inside both root and nested object', () => { + const obj = { + 'a.b': { 'c.d': 123.4 }, + } + expect(_.crush(obj)).toEqual({ + 'a.b.c.d': 123.4, + }) + }) + test('crush array containing object with nested property', () => { + const arr = [{ 'c.d': { 'e.f': 'g' } }] + const obj = { + 'a.b': arr, + } + expect(_.crush(obj)).toEqual({ + 'a.b.0.c.d.e.f': 'g', + }) + }) + test('do not crush Date objects', () => { + const date = new Date() + const obj = { + 'a.b': date, + } + expect(_.crush(obj)).toEqual({ + 'a.b': date, + }) + }) + test('do not crush Map objects', () => { + const map = new Map() + map.set('a', 'b') + map.set('c', 'd') + const obj = { + 'a.b': map, + } + expect(_.crush(obj)).toEqual({ + 'a.b': map, + }) + }) + }) })