Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
31 changes: 31 additions & 0 deletions packages/lib/src/equals/deepEquals.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
import { getObjectKeys, isArray, isObject, isPrimitive } from "./utils";

export const deepEquals = (a: unknown, b: unknown) => {
if (typeof a !== typeof b) return false;

if (isPrimitive(a) && isPrimitive(b)) {
return a === b;
}

if (isArray(a) && isArray(b)) {
if (a.length !== b.length) return false;

for (let i = 0; i < a.length; i++) {
if (!deepEquals(a[i], b[i])) return false;
}

return true;
}

if (isObject(a) && isObject(b)) {
const keysA = getObjectKeys(a);
const keysB = getObjectKeys(b);

if (keysA.length !== keysB.length) return false;

for (const key of keysA) {
if (!deepEquals(a[key], b[key])) return false;
}

return true;
}

return a === b;
};
31 changes: 31 additions & 0 deletions packages/lib/src/equals/shallowEquals.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
import { getObjectKeys, isArray, isObject, isPrimitive } from "./utils";

export const shallowEquals = (a: unknown, b: unknown) => {
if (typeof a !== typeof b) return false;

if (isPrimitive(a) && isPrimitive(b)) {
return a === b;
}

if (isArray(a) && isArray(b)) {
if (a.length !== b.length) return false;

for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}

return true;
}

if (isObject(a) && isObject(b)) {
const keysA = getObjectKeys(a);
const keysB = getObjectKeys(b);

if (keysA.length !== keysB.length) return false;

for (const key of keysA) {
if (a[key] !== b[key]) return false;
}

return true;
}

return a === b;
};
9 changes: 9 additions & 0 deletions packages/lib/src/equals/utils.ts

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utils로 타입 유틸 함수들을 잘 분리해두셔서 전체적인 가독성과 재사용성이 좋아진 것 같아요!

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type Primitive = string | number | boolean | null | undefined | bigint | symbol;

export const isArray = (value: unknown): value is unknown[] => Array.isArray(value);

export const isObject = (value: unknown): value is object => typeof value === "object" && value !== null;
Copy link

@minjaeleee minjaeleee Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

객체를 판단하는 로직에서 엣지 케이스가 있을 것 같아요!
엣지 케이스에서 이 로직을 통과하면 getObjectKeys 함수 내부에서 런타임 에러가 발생할 수 있을 것 같은데, 이 부분 조금 더 고민해보면 어떨까요??

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

민재님 말씀대로 Plain object로 의도하고 작성해야겠네요.
isArray가 앞에 있어서 배열 정도는 걸러준다고 생각했는데 그렇게 되면 isObject는 isArray에 의존성을 갖게 돼요. 이거도 문제네요.
독립적인 함수로 보고 Regex나 Function과 같은 엣지 케이스도 잡는게 좋아보입니다.

혹시 제가 얘기한게 민재님 리뷰의 의도와 맞을까요?


export const isPrimitive = (value: unknown): value is Primitive => typeof value !== "object" || value === null;

export const getObjectKeys = <T extends object>(o: T): (keyof T)[] => Object.keys(o) as (keyof T)[];