You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
554 lines
8.9 KiB
554 lines
8.9 KiB
// tslint:disable-next-line:no-implicit-dependencies |
|
import update, { CustomCommands } from "immutability-helper"; |
|
|
|
interface TestObject { |
|
number: number; |
|
object: { |
|
field: number; |
|
otherField: string; |
|
}; |
|
array: string[]; |
|
set: Set<string>; |
|
map: Map<string, number>; |
|
union: Set<string> | Map<string, number>; |
|
intersection: string[] & { |
|
field: number; |
|
}; |
|
complicated: { |
|
array: Array<{ |
|
nestedArray: Array<{ |
|
field: string; |
|
otherField: { |
|
reallyNestedField: string; |
|
anotherReallyNestedField: number; |
|
}; |
|
}>; |
|
map: Map< |
|
{ key: string; extraneousKeyField: string; }, |
|
{ value: number; } |
|
>; |
|
}>; |
|
}; |
|
} |
|
|
|
const o: TestObject = null as any; |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Basic Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
// $ExpectType TestObject |
|
update(o, {}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
number: { |
|
$set: 1 |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
number: { |
|
$set: "string" |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
object: { |
|
$set: { |
|
field: 1, |
|
otherField: "string", |
|
} |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
object: { |
|
$set: { |
|
notAField: 1 |
|
} |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
object: { |
|
$apply: () => ({ |
|
field: 1, |
|
otherField: "string" |
|
}) |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
object: { |
|
$apply: () => ({ |
|
notAField: 1 |
|
}) |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
object: () => ({ |
|
field: 1, |
|
otherField: "string" |
|
}) |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
object: () => ({ |
|
notAField: 1 |
|
}) |
|
}); |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Object Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
object: { |
|
field: { |
|
$set: 1 |
|
} |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
object: { |
|
field: { |
|
$set: "string" |
|
} |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
object: { |
|
notAField: { |
|
$set: "string" |
|
} |
|
} |
|
}); |
|
|
|
// This tests represents the loophole that if any part of your spec matches the object spec, |
|
// extraneous fields that don't make sense are ignored. |
|
// |
|
// I believe this is because mapped types that include optionality are subject to one of the |
|
// "object literal typecheck" heuristic, specifically, if there are zero compatible fields it's |
|
// an error but if there is at least one then extraneous fields are permitted. |
|
// $ExpectType TestObject |
|
update(o, { |
|
object: { |
|
field: { |
|
$set: 1 |
|
}, |
|
notAField: "nonsense" |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
object: { |
|
$unset: [ "field" ], |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
object: { |
|
$unset: [ "notAField" ], |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
object: { |
|
$toggle: [ "field" ], |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
object: { |
|
$toggle: [ "notAField" ], |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
object: { |
|
$merge: { |
|
field: 1, |
|
}, |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
object: { |
|
$merge: { |
|
notAField: 1, |
|
}, |
|
} |
|
}); |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Array Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
$push: ["string"] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
array: { |
|
$push: [1] |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
$unshift: ["string"] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
array: { |
|
$unshift: [1] |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
$splice: [ [ 1 ] ] |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
$splice: [ [ 1, 2 ] ] |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
$splice: [ [ 1, 2, "string" ] ] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
array: { |
|
$splice: "string" |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
array: { |
|
$splice: [ [ 1, 2, null ] ] |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
1: { |
|
$set: "string" |
|
} |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
array: { |
|
1: { |
|
$set: 1 |
|
} |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
[1]: { |
|
$set: "string" |
|
} |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
['1']: { |
|
$set: "string" |
|
} |
|
} |
|
}); |
|
|
|
// TODO: This test should not pass, but it's because ArraySpec has [index: string]. Changing that |
|
// to [index: number] fails many more tests by being too permissive, so I figure this is the |
|
// preferable error to accept. I do not know why [index: number] would cause the types to be |
|
// permissive to the point of uselessness. |
|
// $ExpectType TestObject |
|
update(o, { |
|
array: { |
|
uhOh: { |
|
$set: "string" |
|
} |
|
} |
|
}); |
|
|
|
// This test passes (i.e., doesn't type check), but for the wrong reasons. See the previous test |
|
// for details. |
|
// $ExpectError |
|
update(o, { |
|
array: { |
|
uhOh: { |
|
$set: 1 |
|
} |
|
} |
|
}); |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Set Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
set: { |
|
$add: [ "string" ] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
set: { |
|
$add: [ 1 ] |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
set: { |
|
$remove: [ "string" ] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
set: { |
|
$remove: [ 1 ] |
|
} |
|
}); |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Map Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
map: { |
|
$add: [ [ "string", 1 ] ] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
map: { |
|
$add: [ [ 1, 1 ] ] |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
map: { |
|
$remove: [ "string" ] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
map: { |
|
$remove: [ 1 ] |
|
} |
|
}); |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Union Type Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
union: { |
|
$set: new Set(), |
|
} |
|
}); |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
union: { |
|
$set: new Map(), |
|
} |
|
}); |
|
|
|
// Matches the Set variant. |
|
// $ExpectType TestObject |
|
update(o, { |
|
union: { |
|
$add: [ "string" ] |
|
} |
|
}); |
|
|
|
// Matches the Map variant. |
|
// $ExpectType TestObject |
|
update(o, { |
|
union: { |
|
$add: [ [ "string", 1 ] ] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
union: { |
|
$add: [ [ "string", "string" ] ] |
|
} |
|
}); |
|
|
|
// Matches both the Set and Map variants. |
|
// $ExpectType TestObject |
|
update(o, { |
|
union: { |
|
$remove: [ "string" ] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
union: { |
|
$remove: 1 |
|
} |
|
}); |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Intersection Type Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
const intersected: string[] & { field: number } = null as any; |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
intersection: { |
|
$set: intersected |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
intersection: { |
|
$set: [] |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update(o, { |
|
intersection: { |
|
$set: { |
|
field: 1 |
|
} |
|
} |
|
}); |
|
|
|
// Matches the object half but not the array half. |
|
// This fails to type check because object specs are only allowed if there is no more-specific spec |
|
// that matches. In this case, the array spec matches, so we don't get the object-related features. |
|
// This is arguably a bug, but this is a pretty strange type so we should be okay not supporting it. |
|
// $ExpectError |
|
update(o, { |
|
intersection: { |
|
field: { |
|
$set: 1 |
|
} |
|
} |
|
}); |
|
|
|
// Matches the array half but not the object half. |
|
// $ExpectType TestObject |
|
update(o, { |
|
intersection: { |
|
$unshift: ["string"] |
|
} |
|
}); |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Complex Spec Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
// $ExpectType TestObject |
|
update(o, { |
|
complicated: { |
|
array: { |
|
1: { |
|
nestedArray: { |
|
1: { |
|
field: { |
|
$set: "string" |
|
}, |
|
otherField: { |
|
$merge: { |
|
anotherReallyNestedField: 1 |
|
} |
|
} |
|
} |
|
}, |
|
map: { |
|
$add: [[ |
|
{ key: "string", extraneousKeyField: "string" }, |
|
{ value: 1 } |
|
]] |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Custom Commands Tests |
|
// ---------------------------------------------------------------------------- |
|
|
|
type TestCustomCommands = CustomCommands<{ $foo: string }>; |
|
|
|
// $ExpectType TestObject |
|
update<TestObject, TestCustomCommands>(o, { |
|
object: { |
|
field: { |
|
$foo: "string" |
|
} |
|
} |
|
}); |
|
|
|
// $ExpectError |
|
update<TestObject, TestCustomCommands>(o, { |
|
object: { |
|
field: { |
|
$foo: 1 |
|
} |
|
} |
|
});
|
|
|