oxc/no-map-spread 성능
작동 방식
Array.prototype.map 및 Array.prototype.flatMap에서 객체나 배열 전개를 사용하여 배열 항목에 속성/요소를 추가하는 것을 금지합니다.
이 규칙은 전개 연산자가 병합 목적으로 사용되는 경우만 보고하며, 복사 목적으로 사용되는 경우는 대상으로 하지 않습니다.
왜 문제가 될까?
전개는 배열 내 객체에 속성을 추가하거나 여러 객체를 결합하는 데 흔히 사용됩니다. 그러나 전개는 새 객체에 대한 재할당과 O(n) 메모리 복사가 발생하므로 비효율적입니다.
// scores 내 각 객체는 얕은 복사됩니다. 그런데 scores는 이후 다시 사용되지 않으므로 전개는 비효율적입니다.
function getDisplayData() {
const scores: Array<{ username: string; score: number }> = getScores();
const displayData = scores.map((score) => ({ ...score, rank: getRank(score) }));
return displayData;
}맵핑된 배열 내 객체가 나중에 변경될 것이라고 기대하지 않는 한, Object.assign을 사용하는 것이 더 좋습니다.
// score는 직접 변경되며 성능이 더 좋습니다.
function getDisplayData() {
const scores: Array<{ username: string; score: number }> = getScores();
const displayData = scores.map((score) => Object.assign(score, { rank: getRank(score) }));
return displayData;
}변경 사항 방지
map 호출에서 객체를 전개하는 유효한 사례가 있습니다. 특히, 반환된 배열의 소비자가 원본 데이터에 영향을 주지 않고 변경할 수 있도록 하기 위해서입니다. 이 규칙은 이러한 케이스를 보고하지 않도록 최선을 다합니다.
클래스 인스턴스 속성에 대한 전개는 완전히 무시됩니다:
class AuthorsDb {
#authors = [];
public getAuthorsWithBooks() {
return this.#authors.map((author) => ({
// 변경 사항을 방지하고 호출자에게 저수준(또는 깊은) 복제본을 제공합니다.
...author,
books: getBooks(author),
}));
}
}map 호출 후 다시 읽어들이는 배열에 대한 전개도 기본적으로 무시됩니다. 이 동작은 ignoreRereads 옵션으로 구성할 수 있습니다.
/* "oxc/no-map-spread": ["error", { "ignoreRereads": true }] */
const scores = getScores();
const displayData = scores.map(score => ({ ...score, rank: getRank(score) }));
console.log(scores); // map 호출 후에 scores가 다시 읽힘배열
배열 전개의 경우, 가능한 한 Array.prototype.concat 또는 Array.prototype.push를 사용해야 합니다. 전개는 반복 가능 객체에 대해 작동하는 반면, concat과 push는 배열에 대해서만 작동하기 때문에 약간 다른 의미를 가집니다.
let arr = [1, 2, 3];
let set = new Set([4]);
let a = [...arr, ...set]; // [1, 2, 3, 4]
let b = arr.concat(set); // [1, 2, 3, Set(1)]
// 전개보다 더 효율적이지만 동일한 의미를 유지합니다. 다만 더 장황합니다.
let c = arr.concat(Array.from(set)); // [1, 2, 3, 4]
// 또한 `Symbol.isConcatSpreadable`을 사용할 수도 있습니다.
set[Symbol.isConcatSpreadable] = true;
let d = arr.concat(set); // [1, 2, 3, 4]자동 수정
이 규칙은 객체 전개로 인해 발생하는 위반 사항을 자동으로 수정할 수 있지만, 배열 전개는 수정하지 않습니다. 배열 수정은 미래에 추가될 수 있습니다.
단일 요소(전개만 포함)인 객체 표현식은 수정되지 않습니다.
arr.map((x) => ({ ...x })); // 수정되지 않음객체에 전개 전에 "정상적인" 요소가 있는 경우 (--fix 사용 시) 수정이 제공됩니다. Object.assign은 첫 번째 인수를 변경하므로, 해당 요소들로 새 객체가 생성되며, 전개 식별자는 변경되지 않습니다. 결과적으로 전개의 의미가 유지됩니다.
// 수정 전
arr.map(({ x, y }) => ({ x, ...y }));
// 수정 후
arr.map(({ x, y }) => Object.assign({ x }, y));객체에서 전개가 첫 번째 속성인 경우, 제안(즉, --fix-suggestions 사용 시)이 제공됩니다. 이 수정은 전개 식별자를 변경하므로 부작용이 발생할 수 있습니다.
// 수정 전
arr.map(({ x, y }) => ({ ...x, y }));
arr.map(({ x, y }) => ({ ...x, y }));
// 수정 후
arr.map(({ x, y }) => Object.assign(x, { y }));
arr.map(({ x, y }) => Object.assign(x, y));예시
이 규칙에 잘못된 코드 예시:
const arr = [{ a: 1 }, { a: 2 }, { a: 3 }];
const arr2 = arr.map((obj) => ({ ...obj, b: obj.a * 2 }));이 규칙에 올바른 코드 예시:
const arr = [{ a: 1 }, { a: 2 }, { a: 3 }];
arr.map((obj) => Object.assign(obj, { b: obj.a * 2 }));
// 인스턴스 속성은 무시됨
class UsersDb {
#users = [];
public get users() {
// users를 복제하여 호출자에게 자체적인 깊은(또는 중간 정도의) 복제본을 제공합니다.
return this.#users.map((user) => ({ ...user }));
}
}function UsersTable({ users }) {
const usersWithRoles = users.map((user) => ({ ...user, role: getRole(user) }));
return (
<table>
{usersWithRoles.map((user) => (
<tr>
<td>{user.name}</td>
<td>{user.role}</td>
</tr>
))}
<tfoot>
<tr>
{/* users 재읽기 */}
<td>총 사용자 수: {users.length}</td>
</tr>
</tfoot>
</table>
);
}참고 자료
설정
이 규칙은 다음 속성을 가진 구성 개체를 수락합니다:
ignoreArgs
type: boolean
기본값: true
함수 매개변수로 전달된 배열에 대한 맵을 무시합니다.
이 옵션은 잘못된 경고를 피하기 위해 기본적으로 활성화되어 있습니다. 하지만 비효율적인 전개를 놓칠 수 있다는 단점이 있습니다. .oxlintrc.json 파일에서 이 옵션을 비활성화하는 것을 권장합니다.
예시
ignoreArgs가 true일 때 이 규칙에 잘못된 코드 예시:
/* "oxc/no-map-spread": ["error", { "ignoreArgs": true }] */
function foo(arr) {
let arr2 = arr.filter((x) => x.a > 0);
return arr2.map((x) => ({ ...x }));
}ignoreArgs가 true일 때 이 규칙에 올바른 코드 예시:
/* "oxc/no-map-spread": ["error", { "ignoreArgs": true }] */
function foo(arr) {
return arr.map((x) => ({ ...x }));
}ignoreRereads
type: boolean
기본값: true
map 호출 후 다시 읽혀지는 맵된 배열을 무시합니다.
재사용되는 배열은 변형을 피하기 위해 얕은 복사 동작에 의존할 수 있습니다. 이러한 경우 Object.assign은 전개보다 실제로 더 효율적이지 않습니다.
사용 방법
이 규칙을 구성 파일이나 명령줄에서 활성화하려면 다음을 사용할 수 있습니다:
{
"rules": {
"oxc/no-map-spread": "error"
}
}oxlint --deny oxc/no-map-spread