Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitdd70a2b

Browse files
authored
fix(apply): fix apply with patches non-string keys (#151)
1 parent25e3cb2 commitdd70a2b

File tree

3 files changed

+138
-5
lines changed

3 files changed

+138
-5
lines changed

‎src/apply.ts‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,34 @@ export function apply<
5757
for(letindex=0;index<path.length-1;index+=1){
5858
constparentType=getType(base);
5959
letkey=path[index];
60-
if(typeofkey!=='string'&&typeofkey!=='number'){
61-
key=String(key);
62-
}
60+
constkeyForCheck=
61+
typeofkey==='symbol' ?undefined :String(keyasany);
6362
if(
6463
((parentType===DraftType.Object||
6564
parentType===DraftType.Array)&&
66-
(key==='__proto__'||key==='constructor'))||
67-
(typeofbase==='function'&&key==='prototype')
65+
keyForCheck!==undefined&&
66+
(keyForCheck==='__proto__'||keyForCheck==='constructor'))||
67+
(typeofbase==='function'&&
68+
keyForCheck!==undefined&&
69+
keyForCheck==='prototype')
6870
){
6971
thrownewError(
7072
`Patching reserved attributes like __proto__ and constructor is not allowed.`
7173
);
7274
}
75+
if(
76+
(parentType===DraftType.Object||
77+
parentType===DraftType.Array||
78+
typeofbase==='function')&&
79+
typeofkey!=='string'&&
80+
typeofkey!=='number'&&
81+
typeofkey!=='symbol'
82+
){
83+
// keyForCheck cannot be undefined here, because:
84+
// - If key is a symbol, this conditional block will not be entered
85+
// - All other types will be converted to String(key)
86+
key=keyForCheck!;
87+
}
7388
// use `index` in Set draft
7489
base=get(parentType===DraftType.Set ?Array.from(base) :base,key);
7590
if(typeofbase!=='object'){

‎test/immer-non-support.test.ts‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,3 +735,69 @@ test('Unexpected undefined not assigned', () => {
735735
expect(Object.prototype.hasOwnProperty.call(foo,'name')).toBe(true);
736736
}
737737
});
738+
739+
test('apply - map with object key',()=>{
740+
{
741+
enablePatches();
742+
enableMapSet();
743+
constkey={id:1};
744+
constbase={
745+
map:newMap([[key,{value:1}]]),
746+
};
747+
const[next,patches,inverse]=produceWithPatches(base,(draft)=>{
748+
draft.map.get(key)!.value=2;
749+
});
750+
expect(()=>applyPatches(base,patches)).toThrow();
751+
expect(()=>applyPatches(next,inverse)).toThrow();
752+
}
753+
{
754+
constkey={id:1};
755+
constbase={
756+
map:newMap([[key,{value:1}]]),
757+
};
758+
const[next,patches,inverse]=create(
759+
base,
760+
(draft)=>{
761+
draft.map.get(key)!.value=2;
762+
},
763+
{enablePatches:true}
764+
);
765+
expect(apply(base,patches)).toEqual(next);
766+
expect(apply(next,inverse)).toEqual(base);
767+
}
768+
});
769+
770+
test('apply - symbol key on object',()=>{
771+
{
772+
enablePatches();
773+
constsym=Symbol('key');
774+
constbase={
775+
obj:{
776+
[sym]:{value:1},
777+
},
778+
};
779+
const[next,patches,inverse]=produceWithPatches(base,(draft)=>{
780+
draft.obj[sym].value=2;
781+
});
782+
expect(()=>applyPatches(base,patches)).toThrow();
783+
expect(()=>applyPatches(next,inverse)).toThrow();
784+
}
785+
{
786+
constsym=Symbol('key');
787+
constbase={
788+
obj:{
789+
[sym]:{value:1},
790+
},
791+
};
792+
const[next,patches,inverse]=create(
793+
base,
794+
(draft)=>{
795+
draft.obj[sym].value=2;
796+
},
797+
{enablePatches:true}
798+
);
799+
expect(apply(base,patches)).toEqual(next);
800+
expect(apply(next,inverse)).toEqual(base);
801+
}
802+
});
803+

‎test/index.test.ts‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4086,6 +4086,58 @@ test('#59 - Failure to apply inverse patchset(Map)', () => {
40864086
expect(reverted2).toEqual(newState);
40874087
});
40884088

4089+
test('apply - map with object key',()=>{
4090+
constkey={id:1};
4091+
constbase={
4092+
map:newMap([[key,{value:1}]]),
4093+
};
4094+
const[next,patches,inverse]=create(
4095+
base,
4096+
(draft)=>{
4097+
draft.map.get(key)!.value=2;
4098+
},
4099+
{enablePatches:true}
4100+
);
4101+
expect(apply(base,patches)).toEqual(next);
4102+
expect(apply(next,inverse)).toEqual(base);
4103+
});
4104+
4105+
test('apply - symbol key on object',()=>{
4106+
constsym=Symbol('key');
4107+
constbase={
4108+
obj:{
4109+
[sym]:{value:1},
4110+
},
4111+
};
4112+
const[next,patches,inverse]=create(
4113+
base,
4114+
(draft)=>{
4115+
draft.obj[sym].value=2;
4116+
},
4117+
{enablePatches:true}
4118+
);
4119+
expect(apply(base,patches)).toEqual(next);
4120+
expect(apply(next,inverse)).toEqual(base);
4121+
});
4122+
4123+
test('apply - coerces non-primitive intermediate key for object',()=>{
4124+
constobjectKey:any={toString:()=>'customKey'};
4125+
constbase={
4126+
foo:{
4127+
customKey:{value:1},
4128+
},
4129+
};
4130+
constpatches:any=[
4131+
{op:'replace',path:['foo',objectKey,'value'],value:2},
4132+
];
4133+
constapplied=apply(base,patches);
4134+
expect(applied).toEqual({
4135+
foo:{
4136+
customKey:{value:2},
4137+
},
4138+
});
4139+
});
4140+
40894141
test('#61 - type issue: current of Draft<T> type should return T type',()=>{
40904142
functiontest<Textends{x:{y:ReadonlySet<string>}}>(base:T):T{
40914143
const[draft,f]=create(base);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp