mirror of
https://github.com/gosticks/DefinitelyTyped.git
synced 2026-07-07 10:40:13 +00:00
[@types/seamless-immutable] Overrides<T> doesn't change some parameters to Immutable<T> or Immutable<T[]> (#34884)
* Added forEach and changed signatures for existing T[] functions T becomes Immutable<T>, T[] becomes Immutable<T[]>. * Corrected whitespace * More whitespace.. * asMutable, concat, isImmutable signatures Expanded asMutable to have 3x options with the same signature, allowing correct typed result when possible. * Further improvements to concat * Further addition of Immutable<T> to concat * Fixing up errors from automated tests around complex Array types * Travis CI objected to default generic parameters being included - fixed * Edit as review feedback (TDeep) and corrected array indexer Changed TDepthBoolean to TDeep as suggested. Removed mutable indexer and added immutable accessor for ImmutableArray - note that adding this directly into * Reverting indexer changes that broke tslinting.. * Reverting indexer changes that broke tslinting... * Re-adding indexer tweaks Linter seems happy when not running Indexer through Readonly<T>. While I was at it, I removed the Readonly<T> from Additions and Overrides given that they are specified immediately below and are completely readonly.. * Corrected default asMutable, added some tests Responded to PR feedback about asMutable with no parameters. Added a few test cases for asMutable to ensure that cases have correct result type. Fixed an incorrect previous test. * Removing 'number' seems necessary as the result type differs * Updated tests to use $ExpectsError (or not) to check immutability of results * Update seamless-immutable-tests.ts Added additional test. Cleaned up some comments.
This commit is contained in:
committed by
Jesse Trinity
parent
4ef82c966a
commit
2eb83051a5
39
types/seamless-immutable/index.d.ts
vendored
39
types/seamless-immutable/index.d.ts
vendored
@@ -32,8 +32,8 @@ declare namespace SeamlessImmutable {
|
||||
prototype?: any;
|
||||
}
|
||||
|
||||
interface AsMutableOptions {
|
||||
deep: boolean;
|
||||
interface AsMutableOptions<TDeep extends boolean = boolean> {
|
||||
deep: TDeep;
|
||||
}
|
||||
|
||||
interface ImmutableObjectMixin<T> {
|
||||
@@ -63,7 +63,9 @@ declare namespace SeamlessImmutable {
|
||||
getIn(propertyPath: string[]): Immutable<any>;
|
||||
getIn<TValue>(propertyPath: string[], defaultValue: TValue): Immutable<TValue>;
|
||||
|
||||
asMutable(opts?: AsMutableOptions): T;
|
||||
asMutable(opts?: AsMutableOptions<false>): { [K in keyof T]: Immutable<T[K]> };
|
||||
asMutable(opts: AsMutableOptions<true>): T;
|
||||
asMutable(opts: AsMutableOptions): T | { [K in keyof T]: Immutable<T[K]> };
|
||||
|
||||
merge(part: DeepPartial<T>, config?: MergeConfig): Immutable<T>;
|
||||
|
||||
@@ -91,29 +93,38 @@ declare namespace SeamlessImmutable {
|
||||
type ImmutableObject<T> = ImmutableObjectMixin<T> & { readonly [P in keyof T]: Immutable<T[P]> };
|
||||
|
||||
/** An ImmutableArray provides read-only access to the array elements, and provides functions (such as `map()`) that return immutable data structures. */
|
||||
type ImmutableArray<T> = Readonly<ImmutableArray.Remaining<T> & ImmutableArray.Additions<T> & ImmutableArray.Overrides<T>>;
|
||||
type ImmutableArray<T> = Readonly<ImmutableArray.Remaining<T>> & ImmutableArray.Additions<T> & ImmutableArray.Overrides<T> & ImmutableArray.ReadOnlyIndexer<T>;
|
||||
namespace ImmutableArray {
|
||||
/** New methods added by seamless-immutable. */
|
||||
interface Additions<T> {
|
||||
asMutable(opts?: AsMutableOptions): T[];
|
||||
asMutable(opts?: AsMutableOptions<false>): Array<Immutable<T>>;
|
||||
asMutable(opts: AsMutableOptions<true>): T[];
|
||||
asMutable(opts: AsMutableOptions): T[] | Array<Immutable<T>>;
|
||||
|
||||
asObject<U extends object = {}, K extends keyof U = keyof U>(toKeyValue: (item: T) => [K, U[K]]): Immutable<U>;
|
||||
flatMap<TTarget>(mapFunction: (item: T) => TTarget): Immutable<TTarget extends any[] ? TTarget : TTarget[]>;
|
||||
}
|
||||
|
||||
/** Custom implementation of the array functions, which return Immutable. */
|
||||
interface Overrides<T> {
|
||||
map<TTarget>(mapFuction: (item: T) => TTarget): Immutable<TTarget[]>;
|
||||
filter(filterFunction: (item: T) => boolean): Immutable<T[]>;
|
||||
forEach(callbackfn: (value: Immutable<T>, index: number, array: Immutable<T[]>) => void, thisArg?: any): void;
|
||||
map<TTarget>(mapFuction: (item: Immutable<T>) => TTarget): Immutable<TTarget[]>;
|
||||
filter(filterFunction: (item: Immutable<T>) => boolean): Immutable<T[]>;
|
||||
slice(start?: number, end?: number): Immutable<T[]>;
|
||||
concat(...arr: Array<T|T[]|Immutable<T|T[]>>): Immutable<T[]>;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): Immutable<T>;
|
||||
reduce<TTarget>(callbackfn: (previousValue: TTarget, currentValue: T, currentIndex: number, array: T[]) => TTarget, initialValue?: TTarget): Immutable<TTarget>;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): Immutable<T>;
|
||||
reduceRight<TTarget>(callbackfn: (previousValue: TTarget, currentValue: T, currentIndex: number, array: T[]) => TTarget, initialValue?: TTarget): Immutable<TTarget>;
|
||||
concat(...arr: Array<T|T[]|Immutable<T>|Array<Immutable<T>>|Immutable<T[]>>): Immutable<T[]>;
|
||||
reduce(callbackfn: (previousValue: Immutable<T>, currentValue: Immutable<T>, currentIndex: number, array: Immutable<T[]>) => T): Immutable<T>;
|
||||
reduce<TTarget>(callbackfn: (previousValue: TTarget, currentValue: Immutable<T>, currentIndex: number, array: Immutable<T[]>) => TTarget, initialValue?: TTarget): Immutable<TTarget>;
|
||||
reduceRight(callbackfn: (previousValue: Immutable<T>, currentValue: Immutable<T>, currentIndex: number, array: Immutable<T[]>) => T): Immutable<T>;
|
||||
reduceRight<TTarget>(callbackfn: (previousValue: TTarget, currentValue: Immutable<T>, currentIndex: number, array: Immutable<T[]>) => TTarget, initialValue?: TTarget): Immutable<TTarget>;
|
||||
}
|
||||
|
||||
/** Merging this into Overrides breaks stuff, so this is split out */
|
||||
interface ReadOnlyIndexer<T> {
|
||||
readonly [key: number]: Immutable<T>;
|
||||
}
|
||||
|
||||
/** These methods are banned by seamless-immutable. */
|
||||
type MutatingArrayMethods = Extract<keyof any[], 'push' | 'pop' | 'sort' | 'splice' | 'shift' | 'unshift' | 'reverse'>;
|
||||
type MutatingArrayMethods = Extract<keyof any[], 'push' | 'pop' | 'sort' | 'splice' | 'shift' | 'unshift' | 'reverse' | number>;
|
||||
|
||||
/** NOTE: These methods mutate data, but seamless-immutable does not ban them. We will ban them in our type definitions. */
|
||||
type AdditionalMutatingArrayMethods = Extract<keyof any[], 'copyWithin' | 'fill'>;
|
||||
@@ -160,7 +171,7 @@ declare namespace SeamlessImmutable {
|
||||
|
||||
function from<T>(obj: T, options?: Options): Immutable<T>;
|
||||
|
||||
function isImmutable(target: any): boolean;
|
||||
function isImmutable<T>(target: T | Immutable<T>): target is Immutable<T>;
|
||||
function ImmutableError(message: string): Error;
|
||||
|
||||
function replace<T, S>(obj: Immutable<T>, valueObj: S, options?: ReplaceConfig): Immutable<S>;
|
||||
|
||||
@@ -15,6 +15,13 @@ interface ExtendedUser extends User {
|
||||
address: Address;
|
||||
}
|
||||
|
||||
// A result type for asMutable({ deep: false } | undefined)
|
||||
interface NonDeepMutableExtendedUser {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
address: Immutable.Immutable<Address>;
|
||||
}
|
||||
|
||||
//
|
||||
// Constructors
|
||||
// ---------------------------------------------------------------
|
||||
@@ -89,6 +96,27 @@ interface ExtendedUser extends User {
|
||||
{
|
||||
const array: Immutable.Immutable<User[]> = Immutable.from([ { firstName: 'Angry', lastName: 'Monkey' } ]);
|
||||
|
||||
const immutableElement: Immutable.Immutable<User> = array[0];
|
||||
// $ExpectError
|
||||
immutableElement.firstName = 'Krusty';
|
||||
|
||||
const asMutableDefault: Array<Immutable.Immutable<User>> = array.asMutable();
|
||||
// Can't mutate beyond 1 level deep (in this case only the array itself is mutable, not the items)
|
||||
// $ExpectError
|
||||
asMutableDefault[0].firstName = 'Krusty';
|
||||
|
||||
const asMutableNotDeep: Array<Immutable.Immutable<User>> = array.asMutable({ deep: false });
|
||||
// Can't mutate beyond 1 level deep (in this case only the array itself is mutable, not the items)
|
||||
// $ExpectError
|
||||
asMutableNotDeep[0].firstName = 'Krusty';
|
||||
|
||||
// Can mutate at any depth
|
||||
const asMutableDeep: User[] = array.asMutable({ deep: true });
|
||||
asMutableDeep[0].firstName = 'Krusty';
|
||||
|
||||
const opaqueMutableParam = { deep: true };
|
||||
const asMutableOpaque: Array<User | Immutable.Immutable<User>> = array.asMutable(opaqueMutableParam);
|
||||
|
||||
// keys. Call the mutable array's 'keys' to ensure compatability
|
||||
const mutableKeys = array.asMutable().keys();
|
||||
const keys: typeof mutableKeys = array.keys();
|
||||
@@ -119,7 +147,7 @@ interface ExtendedUser extends User {
|
||||
slice4.asMutable();
|
||||
|
||||
// concat. Call the mutable array's 'concat' with the same args to ensure compatability. Make sure the output array is immutable.
|
||||
array.asMutable().concat({ firstName: 'Happy', lastName: 'Cat' });
|
||||
array.asMutable().concat(Immutable({ firstName: 'Happy', lastName: 'Cat' }));
|
||||
const concat: Immutable.Immutable<User[]> = array
|
||||
.concat({ firstName: 'Happy', lastName: 'Cat' }, { firstName: 'Silly', lastName: 'Cat' })
|
||||
.concat([{firstName: 'saucy', lastName: 'Cat'}], {firstName: 'Fussy', lastName: 'Cat'})
|
||||
@@ -204,8 +232,25 @@ interface ExtendedUser extends User {
|
||||
const updatedUser12: Immutable.Immutable<ExtendedUser> = immutableUserEx.setIn([ data.propertyId, 'line1' ], 'Small house');
|
||||
|
||||
// asMutable
|
||||
const mutableUser21: User = immutableUser.asMutable();
|
||||
const mutableUser22: User = immutableUser.asMutable({ deep: true });
|
||||
// Can't mutate beyond 1 level deep
|
||||
const mutableUser21 = immutableUserEx.asMutable();
|
||||
mutableUser21.firstName = 'Krusty';
|
||||
// $ExpectError
|
||||
mutableUser21.address.line1 = 'Example';
|
||||
|
||||
// Can't mutate beyond 1 level deep
|
||||
const mutableUser22 = immutableUserEx.asMutable({ deep: false });
|
||||
mutableUser22.firstName = 'Krusty';
|
||||
// $ExpectError
|
||||
mutableUser22.address.line1 = 'Example';
|
||||
|
||||
// Can mutate at any depth
|
||||
const mutableUser23: ExtendedUser = immutableUserEx.asMutable({ deep: true });
|
||||
mutableUser23.firstName = 'Krusty';
|
||||
mutableUser23.address.line1 = 'Example';
|
||||
|
||||
const opaqueMutableParam = { deep: true }; // treats as { deep: boolean }
|
||||
const mutableUser24: ExtendedUser | NonDeepMutableExtendedUser = immutableUserEx.asMutable(opaqueMutableParam);
|
||||
|
||||
// merge: merged part is strongly checked as a deeply partial object
|
||||
const mergedUser: Immutable.Immutable<User> = immutableUserEx.merge({ address: { line1: 'Small house' }, firstName: 'Jack' });
|
||||
|
||||
Reference in New Issue
Block a user