mirror of
https://github.com/toss/es-toolkit.git
synced 2024-11-28 03:34:26 +03:00
feat(has): Implement has
This commit is contained in:
parent
be4162dc19
commit
01cf3a6c9a
@ -1,5 +1,5 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { get as getToolkit } from 'es-toolkit';
|
||||
import { get as getToolkit } from 'es-toolkit/compat';
|
||||
import { get as getLodash } from 'lodash';
|
||||
|
||||
describe('get with string', () => {
|
||||
|
23
benchmarks/performance/has.bench.ts
Normal file
23
benchmarks/performance/has.bench.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { has as hasToolkit } from 'es-toolkit/compat';
|
||||
import { has as hasLodash } from 'lodash';
|
||||
|
||||
describe('has with string', () => {
|
||||
bench('es-toolkit/has', () => {
|
||||
hasToolkit({ a: { b: 3 } }, 'a.b');
|
||||
});
|
||||
|
||||
bench('lodash/has', () => {
|
||||
hasLodash({ a: { b: 3 } }, 'a.b');
|
||||
});
|
||||
});
|
||||
|
||||
describe('has with string array', () => {
|
||||
bench('es-toolkit/has', () => {
|
||||
hasToolkit({ a: { b: 3 } }, ['a', 'b']);
|
||||
});
|
||||
|
||||
bench('lodash/has', () => {
|
||||
hasLodash({ a: { b: 3 } }, ['a', 'b']);
|
||||
});
|
||||
});
|
@ -160,6 +160,7 @@ function sidebar(): DefaultTheme.Sidebar {
|
||||
{ text: 'pickBy', link: '/reference/object/pickBy' },
|
||||
{ text: 'get (compat)', link: '/reference/compat/object/get' },
|
||||
{ text: 'set (compat)', link: '/reference/compat/object/set' },
|
||||
{ text: 'has (compat)', link: '/reference/compat/object/has' },
|
||||
{ text: 'property (compat)', link: '/reference/compat/object/property' },
|
||||
],
|
||||
},
|
||||
|
@ -169,6 +169,7 @@ function sidebar(): DefaultTheme.Sidebar {
|
||||
{ text: 'pickBy', link: '/ko/reference/object/pickBy' },
|
||||
{ text: 'get (호환성)', link: '/ko/reference/compat/object/get' },
|
||||
{ text: 'set (호환성)', link: '/ko/reference/compat/object/set' },
|
||||
{ text: 'has (호환성)', link: '/ko/reference/compat/object/has' },
|
||||
{ text: 'property (호환성)', link: '/ko/reference/compat/object/property' },
|
||||
],
|
||||
},
|
||||
|
@ -156,6 +156,7 @@ function sidebar(): DefaultTheme.Sidebar {
|
||||
{ text: 'pickBy', link: '/zh_hans/reference/object/pickBy' },
|
||||
{ text: 'get (兼容性)', link: '/zh_hans/reference/compat/object/get' },
|
||||
{ text: 'set (兼容性)', link: '/zh_hans/reference/compat/object/set' },
|
||||
{ text: 'has (兼容性)', link: '/zh_hans/reference/compat/object/has' },
|
||||
{ text: 'property (兼容性)', link: '/zh_hans/reference/compat/object/property' },
|
||||
],
|
||||
},
|
||||
|
60
docs/ko/reference/compat/object/has.md
Normal file
60
docs/ko/reference/compat/object/has.md
Normal file
@ -0,0 +1,60 @@
|
||||
# has
|
||||
|
||||
::: info
|
||||
이 함수는 호환성을 위한 `es-toolkit/compat` 에서만 가져올 수 있어요. 대체할 수 있는 네이티브 JavaScript API가 있거나, 아직 충분히 최적화되지 않았기 때문이에요.
|
||||
|
||||
`es-toolkit/compat`에서 이 함수를 가져오면, [lodash와 완전히 똑같이 동작](../../../compatibility.md)해요.
|
||||
:::
|
||||
|
||||
객체가 주어진 경로에 해당하는 프로퍼티를 가지는지 확인해요.
|
||||
|
||||
경로로는 객체의 프로퍼티 이름, 프로퍼티 이름의 배열, 또는 깊은 경로를 나타내는 문자열을 쓸 수 있어요.
|
||||
|
||||
만약에 경로가 인덱스를 나타내고, 객체가 배열 또는 `arguments` 객체라면, 그 인덱스가 유효한지 (0 이상이고 길이 미만인지) 확인해요. 그래서 모든 인덱스가 정의되어 있지 않은 희소 배열(Sparse array)에도 쓸 수 있어요.
|
||||
|
||||
## 인터페이스
|
||||
|
||||
```typescript
|
||||
function has(object: unknown, path: string | number | symbol | Array<string | number | symbol>): boolean;
|
||||
```
|
||||
|
||||
### 파라미터
|
||||
|
||||
- `object` (`unknown`): 프로퍼티가 있는지 확인할 객체.
|
||||
- `path` (`string`, `number`, `symbol`, `Array<string | number | symbol>`): 프로퍼티가 있는지 확인할 경로. 프로퍼티 이름, 프로퍼티 이름의 배열, 또는 깊은 경로를 나타내는 문자열을 쓸 수 있어요.
|
||||
|
||||
### 반환 값
|
||||
|
||||
(`boolean`): 객체가 경로에 값을 가지고 있으면 `true`, 아니면 `false`.
|
||||
|
||||
## 예시
|
||||
|
||||
```typescript
|
||||
import { has } from 'es-toolkit/compat';
|
||||
|
||||
const obj = { a: { b: { c: 3 } } };
|
||||
|
||||
has(obj, 'a'); // true
|
||||
has(obj, ['a', 'b']); // true
|
||||
has(obj, ['a', 'b', 'c']); // true
|
||||
has(obj, 'a.b.c'); // true
|
||||
has(obj, 'a.b.d'); // false
|
||||
has(obj, ['a', 'b', 'c', 'd']); // false
|
||||
has([], 0); // false
|
||||
has([1, 2, 3], 2); // true
|
||||
has([1, 2, 3], 5); // false
|
||||
```
|
||||
|
||||
## 데모
|
||||
|
||||
::: sandpack
|
||||
|
||||
```ts index.ts
|
||||
import { has } from 'es-toolkit/compat';
|
||||
|
||||
const obj = { a: { b: { c: 3 } } };
|
||||
|
||||
console.log(has(obj, 'a.b.c'));
|
||||
```
|
||||
|
||||
:::
|
62
docs/reference/compat/object/has.md
Normal file
62
docs/reference/compat/object/has.md
Normal file
@ -0,0 +1,62 @@
|
||||
# has
|
||||
|
||||
::: info
|
||||
This function is only available in `es-toolkit/compat` for compatibility reasons. It either has alternative native JavaScript APIs or isn’t fully optimized yet.
|
||||
|
||||
When imported from `es-toolkit/compat`, it behaves exactly like lodash and provides the same functionalities, as detailed [here](../../../compatibility.md).
|
||||
:::
|
||||
|
||||
Checks if a given path exists within an object.
|
||||
|
||||
You can provide the path as a single property key, an array of property keys,
|
||||
or a string representing a deep path.
|
||||
|
||||
If the path is an index and the object is an array or an arguments object,
|
||||
the function will verify if the index is valid and within the bounds of the array
|
||||
or arguments object, even if the array or arguments object is sparse
|
||||
(i.e., not all indexes are defined).
|
||||
|
||||
## Signature
|
||||
|
||||
function has(object: unknown, path: string | number | symbol | Array<string | number | symbol>): boolean;
|
||||
|
||||
### Parameters
|
||||
|
||||
- `object` (`unknown`): The object to query.
|
||||
- `path` (`string` or `number` or `symbol` or `Array<string | number | symbol>`): The path to check. This can be a single property key, an array of property keys, or a string representing a deep path.
|
||||
|
||||
### Returns
|
||||
|
||||
(`boolean`): Returns `true` if the path exists in the object, `false` otherwise.
|
||||
|
||||
## Examples
|
||||
|
||||
```typescript
|
||||
import { has } from 'es-toolkit/compat';
|
||||
|
||||
const obj = { a: { b: { c: 3 } } };
|
||||
|
||||
has(obj, 'a'); // true
|
||||
has(obj, ['a', 'b']); // true
|
||||
has(obj, ['a', 'b', 'c']); // true
|
||||
has(obj, 'a.b.c'); // true
|
||||
has(obj, 'a.b.d'); // false
|
||||
has(obj, ['a', 'b', 'c', 'd']); // false
|
||||
has([], 0); // false
|
||||
has([1, 2, 3], 2); // true
|
||||
has([1, 2, 3], 5); // false
|
||||
```
|
||||
|
||||
## 데모
|
||||
|
||||
::: sandpack
|
||||
|
||||
```typescript
|
||||
import { has } from 'es-toolkit/compat';
|
||||
|
||||
const obj = { a: { b: { c: 3 } } };
|
||||
|
||||
console.log(has(obj, 'a.b.c'));
|
||||
```
|
||||
|
||||
:::
|
60
docs/zh_hans/reference/compat/object/has.md
Normal file
60
docs/zh_hans/reference/compat/object/has.md
Normal file
@ -0,0 +1,60 @@
|
||||
# has
|
||||
|
||||
::: info
|
||||
出于兼容性原因,此函数仅在 `es-toolkit/compat` 中提供。它可能具有替代的原生 JavaScript API,或者尚未完全优化。
|
||||
|
||||
从 `es-toolkit/compat` 导入时,它的行为与 lodash 完全一致,并提供相同的功能,详情请见 [这里](../../../compatibility.md)。
|
||||
|
||||
:::
|
||||
|
||||
检查给定路径在对象中是否存在。
|
||||
|
||||
您可以提供路径作为单个属性键、属性键数组,或表示深层路径的字符串。
|
||||
|
||||
如果路径是索引,且对象是数组或参数对象,函数将验证索引是否有效,并且在数组或参数对象的范围内,
|
||||
即使数组或参数对象是稀疏的(即,并非所有索引都被定义)。
|
||||
|
||||
## 签名
|
||||
|
||||
function has(object: unknown, path: string | number | symbol | Array<string | number | symbol>): boolean;
|
||||
|
||||
### 参数
|
||||
|
||||
- `object` (`unknown`): 要查询的对象。
|
||||
- `path` (`string` 或 `number` 或 `symbol` 或 `Array<string | number | symbol>`): 要检查的路径。这可以是单个属性键、属性键数组或表示深层路径的字符串。
|
||||
|
||||
### 返回值
|
||||
|
||||
(`boolean`): 如果路径在对象中存在,则返回 `true`,否则返回 `false`。
|
||||
|
||||
## 示例
|
||||
|
||||
```typescript
|
||||
import { has } from 'es-toolkit/compat';
|
||||
|
||||
const obj = { a: { b: { c: 3 } } };
|
||||
|
||||
has(obj, 'a'); // true
|
||||
has(obj, ['a', 'b']); // true
|
||||
has(obj, ['a', 'b', 'c']); // true
|
||||
has(obj, 'a.b.c'); // true
|
||||
has(obj, 'a.b.d'); // false
|
||||
has(obj, ['a', 'b', 'c', 'd']); // false
|
||||
has([], 0); // false
|
||||
has([1, 2, 3], 2); // true
|
||||
has([1, 2, 3], 5); // false
|
||||
```
|
||||
|
||||
## 演示
|
||||
|
||||
::: sandpack
|
||||
|
||||
```ts index.ts
|
||||
import { has } from 'es-toolkit/compat';
|
||||
|
||||
const obj = { a: { b: { c: 3 } } };
|
||||
|
||||
console.log(has(obj, 'a.b.c'));
|
||||
```
|
||||
|
||||
:::
|
22
src/compat/_internal/toKey.spec.ts
Normal file
22
src/compat/_internal/toKey.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { toKey } from './toKey';
|
||||
|
||||
describe('toKey', () => {
|
||||
it('converts strings to strings', () => {
|
||||
expect(toKey('asd')).toBe('asd');
|
||||
});
|
||||
|
||||
it('converts symbols to symbols', () => {
|
||||
const symbol = Symbol('a');
|
||||
expect(toKey(symbol)).toBe(symbol);
|
||||
});
|
||||
|
||||
it("converts 0 to '0'", () => {
|
||||
expect(toKey(0)).toBe('0');
|
||||
});
|
||||
|
||||
it("converts -0 to '-0'", () => {
|
||||
expect(toKey(-0)).toBe('-0');
|
||||
expect(toKey(Object(-0))).toBe('-0');
|
||||
});
|
||||
});
|
20
src/compat/_internal/toKey.ts
Normal file
20
src/compat/_internal/toKey.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { isSymbol } from '../predicate/isSymbol';
|
||||
|
||||
/**
|
||||
* Converts `value` to a string key if it's not a string or symbol.
|
||||
*
|
||||
* @private
|
||||
* @param {*} value The value to inspect.
|
||||
* @returns {string|symbol} Returns the key.
|
||||
*/
|
||||
export function toKey(value: unknown) {
|
||||
if (typeof value === 'string' || isSymbol(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (Object.is(value?.valueOf(), -0)) {
|
||||
return '-0';
|
||||
}
|
||||
|
||||
return `${value}`;
|
||||
}
|
@ -41,6 +41,7 @@ export { bind } from './function/bind.ts';
|
||||
|
||||
export { get } from './object/get.ts';
|
||||
export { set } from './object/set.ts';
|
||||
export { has } from './object/has.ts';
|
||||
export { property } from './object/property.ts';
|
||||
export { mapKeys } from './object/mapKeys.ts';
|
||||
export { mapValues } from './object/mapValues.ts';
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { isDeepKey } from '../_internal/isDeepKey.ts';
|
||||
import { toKey } from '../_internal/toKey.ts';
|
||||
import { toPath } from '../_internal/toPath.ts';
|
||||
import type { Get } from './get.types.ts';
|
||||
|
||||
@ -308,11 +309,7 @@ export function get(object: any, path: PropertyKey | readonly PropertyKey[], def
|
||||
let index;
|
||||
|
||||
for (index = 0; index < resolvedPath.length && current != null; index++) {
|
||||
let key = resolvedPath[index];
|
||||
|
||||
if (Object.is(key.valueOf(), -0)) {
|
||||
key = '-0';
|
||||
}
|
||||
let key = toKey(resolvedPath[index]);
|
||||
|
||||
current = current[key];
|
||||
}
|
||||
|
206
src/compat/object/has.spec.ts
Normal file
206
src/compat/object/has.spec.ts
Normal file
@ -0,0 +1,206 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { toArgs } from '../_internal/toArgs';
|
||||
import { has } from './has';
|
||||
import { stubTrue } from '../_internal/stubTrue';
|
||||
import { range } from '../../math/range';
|
||||
import { args } from '../_internal/args';
|
||||
import { symbol } from '../_internal/symbol';
|
||||
import { stubFalse } from '../_internal/stubFalse';
|
||||
|
||||
describe('has', () => {
|
||||
it(`should check for own properties`, () => {
|
||||
const object = { a: 1 };
|
||||
|
||||
['a', ['a']].forEach(path => {
|
||||
expect(has(object, path)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should not use the \`hasOwnProperty\` method of \`object\``, () => {
|
||||
const object = { hasOwnProperty: null, a: 1 };
|
||||
expect(has(object, 'a')).toBe(true);
|
||||
});
|
||||
|
||||
it(`should support deep paths`, () => {
|
||||
const object = { a: { b: 2 } };
|
||||
|
||||
['a.b', ['a', 'b']].forEach(path => {
|
||||
expect(has(object, path)).toBe(true);
|
||||
});
|
||||
|
||||
['a.a', ['a', 'a']].forEach(path => {
|
||||
expect(has(object, path)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should coerce \`path\` to a string`, () => {
|
||||
function fn() {}
|
||||
fn.toString = () => 'fn';
|
||||
|
||||
const object = { null: 1, undefined: 2, fn: 3, '[object Object]': 4 };
|
||||
const paths: any[] = [null, undefined, fn, {}];
|
||||
const expected = paths.map(stubTrue);
|
||||
|
||||
range(2).forEach(index => {
|
||||
const actual = paths.map(path => has(object, index ? [path] : path));
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should work with \`arguments\` objects`, () => {
|
||||
expect(has(args, 1)).toBe(true);
|
||||
});
|
||||
|
||||
it(`should work with a non-string \`path\``, () => {
|
||||
const array = [1, 2, 3];
|
||||
|
||||
[1, [1]].forEach(path => {
|
||||
expect(has(array, path)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should preserve the sign of \`0\``, () => {
|
||||
const object = { '-0': 'a', 0: 'b' };
|
||||
const props = [-0, Object(-0), 0, Object(0)];
|
||||
const expected = props.map(stubTrue);
|
||||
|
||||
const actual = props.map(key => has(object, key));
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it(`should work with a symbol \`path\``, () => {
|
||||
function Foo() {}
|
||||
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
Foo.prototype[symbol] = 1;
|
||||
|
||||
const symbol2 = Symbol('b');
|
||||
Object.defineProperty(Foo.prototype, symbol2, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: 2,
|
||||
});
|
||||
|
||||
const object = Foo.prototype;
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
expect(has(object, symbol)).toBe(true);
|
||||
expect(has(object, symbol2)).toBe(true);
|
||||
});
|
||||
|
||||
it(`has should check for a key over a path`, () => {
|
||||
const object = { 'a.b': 1 };
|
||||
|
||||
['a.b', ['a.b']].forEach(path => {
|
||||
expect(has(object, path)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should return \`true\` for indexes of sparse values`, () => {
|
||||
const sparseArgs = toArgs([1]);
|
||||
const sparseArray = Array(1);
|
||||
const sparseString = Object('a');
|
||||
|
||||
delete sparseArgs[0];
|
||||
|
||||
const values = [sparseArgs, sparseArray, sparseString];
|
||||
|
||||
const expected = values.map(stubTrue);
|
||||
|
||||
const actual = values.map(value => has(value, 0));
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it(`should return \`true\` for indexes of sparse values with deep paths`, () => {
|
||||
const sparseArgs = toArgs([1]);
|
||||
const sparseArray = Array(1);
|
||||
const sparseString = Object('a');
|
||||
|
||||
delete sparseArgs[0];
|
||||
|
||||
const values = [sparseArgs, sparseArray, sparseString];
|
||||
const expected = values.map(() => [true, true]);
|
||||
|
||||
const actual = values.map(value => ['a[0]', ['a', '0']].map(path => has({ a: value }, path)));
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it(`should return false for inherited properties`, () => {
|
||||
function Foo() {}
|
||||
Foo.prototype.a = 1;
|
||||
|
||||
['a', ['a']].forEach(path => {
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
expect(has(new Foo(), path)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should return false for nested inherited properties`, () => {
|
||||
function Foo() {}
|
||||
Foo.prototype.a = { b: 1 };
|
||||
|
||||
['a.b', ['a', 'b']].forEach(path => {
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
expect(has(new Foo(), path)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should return \`false\` when \`object\` is nullish`, () => {
|
||||
const values = [null, undefined];
|
||||
const expected = values.map(stubFalse);
|
||||
|
||||
['constructor', ['constructor']].forEach(path => {
|
||||
const actual = values.map(value => has(value, path));
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should return \`false\` for deep paths when \`object\` is nullish`, () => {
|
||||
const values = [null, undefined];
|
||||
const expected = values.map(stubFalse);
|
||||
|
||||
['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']].forEach(path => {
|
||||
const actual = values.map(value => has(value, path));
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should return \`false\` for nullish values of nested objects`, () => {
|
||||
const values = [, null, undefined];
|
||||
const expected = values.map(stubFalse);
|
||||
|
||||
['a.b', ['a', 'b']].forEach(path => {
|
||||
const actual = values.map((value, index) => {
|
||||
const object = index ? { a: value } : {};
|
||||
return has(object, path);
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should return \`false\` over sparse values of deep paths`, () => {
|
||||
const sparseArgs = toArgs([1]);
|
||||
const sparseArray = Array(1);
|
||||
const sparseString = Object('a');
|
||||
|
||||
delete sparseArgs[0];
|
||||
|
||||
const values = [sparseArgs, sparseArray, sparseString];
|
||||
const expected = values.map(() => [false, false]);
|
||||
|
||||
const actual = values.map(value => ['a[0].b', ['a', '0', 'b']].map(path => has({ a: value }, path)));
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
70
src/compat/object/has.ts
Normal file
70
src/compat/object/has.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { isDeepKey } from '../_internal/isDeepKey.ts';
|
||||
import { isIndex } from '../_internal/isIndex.ts';
|
||||
import { toPath } from '../_internal/toPath.ts';
|
||||
import { isArguments } from '../predicate/isArguments.ts';
|
||||
|
||||
/**
|
||||
* Checks if a given path exists within an object.
|
||||
*
|
||||
* You can provide the path as a single property key, an array of property keys,
|
||||
* or a string representing a deep path.
|
||||
*
|
||||
* If the path is an index and the object is an array or an arguments object, the function will verify
|
||||
* if the index is valid and within the bounds of the array or arguments object, even if the array or
|
||||
* arguments object is sparse (i.e., not all indexes are defined).
|
||||
*
|
||||
* @param {Object} object - The object to query.
|
||||
* @param {PropertyKey | PropertyKey[]} path - The path to check. This can be a single property key,
|
||||
* an array of property keys, or a string representing a deep path.
|
||||
* @returns {boolean} Returns `true` if the path exists in the object, `false` otherwise.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* const obj = { a: { b: { c: 3 } } };
|
||||
*
|
||||
* has(obj, 'a'); // true
|
||||
* has(obj, ['a', 'b']); // true
|
||||
* has(obj, ['a', 'b', 'c']); // true
|
||||
* has(obj, 'a.b.c'); // true
|
||||
* has(obj, 'a.b.d'); // false
|
||||
* has(obj, ['a', 'b', 'c', 'd']); // false
|
||||
* has([], 0); // false
|
||||
* has([1, 2, 3], 2); // true
|
||||
* has([1, 2, 3], 5); // false
|
||||
*/
|
||||
export function has(object: unknown, path: PropertyKey | readonly PropertyKey[]): boolean;
|
||||
|
||||
export function has(object: any, path: PropertyKey | readonly PropertyKey[]): boolean {
|
||||
let resolvedPath;
|
||||
|
||||
if (Array.isArray(path)) {
|
||||
resolvedPath = path;
|
||||
} else if (typeof path === 'string' && isDeepKey(path) && object?.[path] == null) {
|
||||
resolvedPath = toPath(path);
|
||||
} else {
|
||||
resolvedPath = [path];
|
||||
}
|
||||
|
||||
if (resolvedPath.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let current = object;
|
||||
|
||||
for (let i = 0; i < resolvedPath.length; i++) {
|
||||
const key = resolvedPath[i];
|
||||
|
||||
// Check if the current key is a direct property of the current object
|
||||
if (current == null || !Object.prototype.hasOwnProperty.call(current, key)) {
|
||||
const isSparseIndex = (Array.isArray(current) || isArguments(current)) && isIndex(key) && key < current.length;
|
||||
|
||||
if (!isSparseIndex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
current = current[key];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
Loading…
Reference in New Issue
Block a user