Skip to content

Commit

Permalink
feat(js-core): Improve typing of mapToObject() for index types
Browse files Browse the repository at this point in the history
Closes #54
  • Loading branch information
ersimont committed Nov 3, 2021
1 parent 50239e5 commit 4310429
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 39 deletions.
8 changes: 8 additions & 0 deletions projects/js-core/src/lib/interfaces.ts
@@ -1,3 +1,5 @@
import { IfIndexType } from '../../../micro-dash/src/lib/interfaces';

export type ArrayIteratee<I, O> = (item: I, index: number) => O;
export type ObjectIteratee<T, O> = (
item: T[keyof T],
Expand All @@ -8,3 +10,9 @@ export type StringifiedKey<T> = Cast<keyof T, string>;
type Cast<I, O> = Exclude<I, O> extends never ? I : O;

export type Nil = null | undefined;

type IndexKeys<T> = { [K in keyof T]: IfIndexType<K, K> }[keyof T];
type NonIndexKeys<T> = { [K in keyof T]: IfIndexType<K, never, K> }[keyof T];
export type PartialExceptIndexes<T> = { [K in IndexKeys<T>]: T[K] } & {
[K in NonIndexKeys<T>]?: T[K];
};
2 changes: 1 addition & 1 deletion projects/js-core/src/lib/migration-manager.ts
Expand Up @@ -51,7 +51,7 @@ export class MigrationManager<T extends VersionedObject> {
* If an error is thrown by a migration, see `onError()` for details.
*/
run(persistence: Persistence<T>, defaultValue: T): T {
let object: T = persistence.get();
let object = persistence.get();
if (object?._version === defaultValue._version) {
return object;
}
Expand Down
48 changes: 48 additions & 0 deletions projects/js-core/src/lib/objects/map-to-object.spec.ts
@@ -1,4 +1,5 @@
import { expectCallsAndReset } from '@s-libs/ng-dev';
import { expectTypeOf } from 'expect-type';
import { mapToObject } from './map-to-object';

describe('mapToObject()', () => {
Expand Down Expand Up @@ -32,4 +33,51 @@ describe('mapToObject()', () => {
mapToObject({ a: 1, b: 2 }, spy);
expectCallsAndReset(spy, [1, 'a'], [2, 'b']);
});

describe('typing', () => {
it('is good for arrays', () => {
type A = number[];
type AorU = A | undefined;
type AorN = A | null;

const a = [] as A;
const aOrU = a as AorU;
const aOrN = a as AorN;

type Result = { a?: number };
expectTypeOf(mapToObject(a, () => ['a', 1])).toEqualTypeOf<Result>();
expectTypeOf(mapToObject(aOrN, () => ['a', 1])).toEqualTypeOf<Result>();
expectTypeOf(mapToObject(aOrU, () => ['a', 1])).toEqualTypeOf<Result>();

const index: [string, number] = ['a', 1];
type IndexResult = Record<string, number>;
expectTypeOf(mapToObject(a, () => index)).toEqualTypeOf<IndexResult>();
expectTypeOf(mapToObject(aOrU, () => index)).toEqualTypeOf<IndexResult>();
expectTypeOf(mapToObject(aOrN, () => index)).toEqualTypeOf<IndexResult>();
});

it('is good for objects', () => {
interface O {
a: string;
b: number;
}
type OorU = O | undefined;
type OorN = O | null;

const o = {} as O;
const oOrU = o as OorU;
const oOrN = o as OorN;

type Result = { a?: number };
expectTypeOf(mapToObject(o, () => ['a', 1])).toEqualTypeOf<Result>();
expectTypeOf(mapToObject(oOrU, () => ['a', 1])).toEqualTypeOf<Result>();
expectTypeOf(mapToObject(oOrN, () => ['a', 1])).toEqualTypeOf<Result>();

const index: [string, number] = ['a', 1];
type IndexResult = Record<string, number>;
expectTypeOf(mapToObject(o, () => index)).toEqualTypeOf<IndexResult>();
expectTypeOf(mapToObject(oOrU, () => index)).toEqualTypeOf<IndexResult>();
expectTypeOf(mapToObject(oOrN, () => index)).toEqualTypeOf<IndexResult>();
});
});
});
13 changes: 9 additions & 4 deletions projects/js-core/src/lib/objects/map-to-object.ts
@@ -1,5 +1,10 @@
import { transform } from '@s-libs/micro-dash';
import { ArrayIteratee, Nil, ObjectIteratee } from '../interfaces';
import {
ArrayIteratee,
Nil,
ObjectIteratee,
PartialExceptIndexes,
} from '../interfaces';

/**
* Maps `collection` a new object, with keys and values determined by `iteratee`.
Expand All @@ -14,13 +19,13 @@ import { ArrayIteratee, Nil, ObjectIteratee } from '../interfaces';
*/

export function mapToObject<I, K extends keyof any, V>(
array: I[] | Nil,
array: readonly I[] | Nil,
iteratee: ArrayIteratee<I, Readonly<[K, V]>>,
): { [k in K]?: V };
): PartialExceptIndexes<{ [k in K]: V }>;
export function mapToObject<T, K extends keyof any, V>(
object: T | Nil,
iteratee: ObjectIteratee<T, Readonly<[K, V]>>,
): { [k in K]?: V };
): PartialExceptIndexes<{ [k in K]: V }>;

export function mapToObject(collection: any, iteratee: any): any {
return transform(collection, (accumulator: any, origValue, keyOrIndex) => {
Expand Down
34 changes: 0 additions & 34 deletions projects/js-core/src/typing-tests/map-to-object.dts-spec.ts

This file was deleted.

0 comments on commit 4310429

Please sign in to comment.