@@ -31,12 +31,14 @@ const _hasOwn = Object.prototype.hasOwnProperty;
3131 * @returns {T }
3232 * @template T
3333 */
34- export function map < T > ( opt_initial : T | undefined ) {
35- const obj = Object . create ( null ) ;
34+ export const map = < T > ( opt_initial : T | null | undefined ) : object => {
35+ const object = Object . create ( null ) ;
3636 if ( opt_initial ) {
37- Object . assign ( obj , opt_initial ) ;
37+ Object . assign ( object , opt_initial ) ;
3838 }
39- return obj ;
39+
40+ // FIXME(@DerekNonGeneric): Should we be creating objects w/ null protos?
41+ return { ...opt_initial } ;
4042}
4143
4244/**
@@ -46,8 +48,8 @@ export function map<T>(opt_initial: T | undefined) {
4648 * @returns {boolean }
4749 * @template T
4850 */
49- export function hasOwn < T > ( obj : T , key : string ) {
50- return _hasOwn . call ( obj , key ) ;
51+ export const hasOwn = < T > ( object : T , key : string ) : boolean => {
52+ return _hasOwn . call ( object , key ) ;
5153}
5254
5355/**
@@ -57,42 +59,43 @@ export function hasOwn<T>(obj: T, key: string) {
5759 * @param {string } key
5860 * @returns {unknown }
5961 */
60- export function ownProperty ( obj : Record < string , number | RegExp > , key : string ) {
61- if ( hasOwn ( obj , key ) ) {
62- return obj [ key ] ;
63- } else {
64- return undefined ;
65- }
62+ export const ownProperty = (
63+ object : Record < string , number | RegExp > ,
64+ key : string ,
65+ ) : unknown => {
66+ return hasOwn ( object , key ) ? Reflect . get ( object , key ) : undefined ;
6667}
6768
68- interface ITargetSourceDepth {
69- t : Object ;
70- s : Object ;
69+ /** @typedef {{t: object, s: object, d: number} } DeepMergeTuple */
70+ type DeepMergeTuple = {
71+ t : object ;
72+ s : object ;
7173 d : number ;
7274}
7375
7476/**
7577 * Deep merges source into target.
7678 *
77- * @param {!Object } target
78- * @param {!Object } source
79- * @param {number } depth The maximum merge depth. If exceeded, Object.assign
80- * will be used instead.
81- * @returns {!Object }
79+ * @param {!object } target
80+ * @param {!object } source
81+ * @param {! number } depth The maximum merge depth. If exceeded, ` Object.assign`
82+ * will be used instead.
83+ * @return {!object }
8284 * @throws {Error } If source contains a circular reference.
8385 * Note: Only nested objects are deep-merged, primitives and arrays are not.
8486 */
85- export function deepMerge ( target : Object , source : Object , depth = 10 ) : Object {
87+ export const deepMerge = ( target : object , source : object , depth = 10 ) : object => {
8688 // Keep track of seen objects to detect recursive references.
87- const seen : Array < Object > = [ ] ;
89+ /** @type {!object[] } */
90+ const seen : object [ ] = [ ] ;
8891
89- /** @type {!Array<ITargetSourceDepth> } */
90- const queue : Array < ITargetSourceDepth > = [ ] ;
92+ /** @type {!DeepMergeTuple[] } */
93+ const queue : DeepMergeTuple [ ] = [ ] ;
9194 queue . push ( { t : target , s : source , d : 0 } ) ;
9295
9396 // BFS to ensure objects don't have recursive references at shallower depths.
9497 while ( queue . length > 0 ) {
95- const { t, s, d } = map ( queue . shift ( ) ) ;
98+ const { t, s, d } = /** @type { !DeepMergeTuple } */ Object ( queue . shift ( ) ) ;
9699 if ( seen . includes ( s ) ) {
97100 throw new Error ( 'Source object has a circular reference.' ) ;
98101 }
@@ -104,19 +107,19 @@ export function deepMerge(target: Object, source: Object, depth = 10): Object {
104107 Object . assign ( t , s ) ;
105108 continue ;
106109 }
107- Object . keys ( s ) . forEach ( ( key ) => {
108- const newValue = s [ key ] ;
110+ for ( const key of Object . keys ( s ) ) {
111+ const newValue = Reflect . get ( s , key ) ;
109112 // Perform a deep merge IFF both target and source have the same key
110113 // whose corresponding values are objects.
111114 if ( hasOwn ( t , key ) ) {
112- const oldValue = t [ key ] ;
115+ const oldValue = Reflect . get ( t , key ) ;
113116 if ( isObject ( newValue ) && isObject ( oldValue ) ) {
114117 queue . push ( { t : oldValue , s : newValue , d : d + 1 } ) ;
115- return ;
118+ continue ;
116119 }
117120 }
118- t [ key ] = newValue ;
119- } ) ;
121+ Reflect . set ( t , key , newValue ) ;
122+ }
120123 }
121124 return target ;
122125}
@@ -126,11 +129,11 @@ export function deepMerge(target: Object, source: Object, depth = 10): Object {
126129 * @param {!Record<string, number | RegExp> | null | undefined } o2
127130 * @returns {boolean }
128131 */
129- export function objectsEqualShallow (
132+ export const objectsEqualShallow = (
130133 o1 : Record < string , number | RegExp > | null | undefined ,
131134 o2 : Record < string , number | RegExp > | null | undefined
132- ) : boolean {
133- if ( o1 === null || o2 === null ) {
135+ ) : boolean => {
136+ if ( o1 == undefined || o2 == undefined ) {
134137 // Null is only equal to null, and undefined to undefined.
135138 return o1 === o2 ;
136139 }
@@ -153,21 +156,21 @@ export function objectsEqualShallow(
153156 * updates the object originally passed, and returns the value that was returned
154157 * by the factory function.
155158 *
156- * @param {T } obj
157- * @param {string } prop
159+ * @param {T extends object } object
160+ * @param {string } property
158161 * @param {function(T, string):R } factory
159162 * @returns {R }
160- * @template T,R
163+ * @template P, T,R
161164 */
162- export function memo < T , P extends keyof T > (
163- obj : T ,
164- prop : P ,
165- factory : ( arg0 : T , arg1 : P ) => T [ P ]
166- ) : T [ P ] {
167- let result = obj [ prop ] ;
165+ export const memo = < T extends object , P extends keyof T > (
166+ object : T ,
167+ property : P ,
168+ factory : ( argument0 : T , argument1 : P ) => T [ P ] ,
169+ ) : T [ P ] => {
170+ let result = Reflect . get ( object , property ) ;
168171 if ( result === undefined ) {
169- result = factory ( obj , prop ) ;
170- obj [ prop ] = result ;
172+ result = factory ( object , property ) ;
173+ Reflect . set ( object , property , result ) ;
171174 }
172175 return result ;
173176}
0 commit comments