Skip to content

Commit

Permalink
type improvements, added remote and order
Browse files Browse the repository at this point in the history
  • Loading branch information
paarthenon committed Dec 31, 2020
1 parent 9362f40 commit 2206ad5
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 23 deletions.
3 changes: 3 additions & 0 deletions docu/docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ title: Changelog
Summary of the changes in each patch.

## 2.0.3
- added `remote()` and `order()`.
- Improved handling of primitives in conditional types
- Added `types()` function to replace `outputTypes()` and extend its functionality.
- Added `matcher()` function
- Added `constrained()`, `patterned()`, and `augmented()`.
- Added match helpers `just()` (alias for `constant()`) and `unpack()`
Expand Down
4 changes: 2 additions & 2 deletions src/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ type VGenericFunction<T, Map extends GenericMapping> =
: T;
;

type VGenericObject<T, Map extends GenericMapping> = {
type VGenericObject<T, Map extends GenericMapping> = Identity<{
[P in keyof T]: Generify<T[P], Map>;
};
}>;

export type Generify<T, Map extends GenericMapping> =
T extends VGeneric<infer Label>
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export * from './matcher';
export * from './lookup';
export * from './loose';
export * from './generic';
export * from './remote';
export * from './order';

import {default as variantDefault} from './variant';
export default variantDefault;
75 changes: 75 additions & 0 deletions src/order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@


/**
* What should a progression do?
*
* - Be able to create a variant
* - maybe this isn't necessary.
* - do a .compare(a: T, b: T) and get back a number;
*/

import {remote, Remote} from './remote';
import {Func} from './util';
import {Creators, KeysOf, Property, VariantCreator, VariantModule, VariantOf} from './variant';

export enum CompareResult {
Lesser = -1,
Equal,
Greater
}

/**
* A valid entry for `variantList`
*/
type validListType = VariantCreator<any, Func, any> | string;

/**
* Convert entries for a `variantList` to the same type.
*/
type Variantify<T extends validListType> = T extends string ? VariantCreator<T> : T;

interface Order<T extends VariantModule<K>, K extends string = 'type'> extends Remote<T, K> {
compare: (
a: VariantOf<T, undefined, K> | Creators<T, K>[keyof T] | KeysOf<T, K>,
b: VariantOf<T, undefined, K> | Creators<T, K>[keyof T] | KeysOf<T, K>,
) => CompareResult;
index: (a: VariantOf<T, undefined, K> | Creators<T, K>[keyof T] | KeysOf<T, K>) => number;
}

export function order<
T extends VariantModule<K>,
L extends readonly (KeysOf<T, K> | Property<K, KeysOf<T, K>>)[],
K extends string = 'type'
>(vmod: T, order: L): Order<T, K> {
let rawStringOrder = order.map(i => typeof i === 'string' ? i : i.type);
const keyType = Object.values(vmod)[0].key;
return {
...remote(vmod, keyType),
compare: (a, b) => {
const ai = rawStringOrder.findIndex(i => i === getType(a, keyType));
const bi = rawStringOrder.findIndex(i => i === getType(b, keyType));
const diff = ai - bi;
return diff === 0 ? diff : (diff / Math.abs(diff)) as CompareResult;
},
index: a => rawStringOrder.findIndex(i => i === getType(a)),
}
}

function getType<
T extends VariantModule<K>,
O extends VariantOf<T, undefined, K>,
C extends Creators<T, K>,
S extends KeysOf<T, K>,
K extends string = 'type',
>(object: O | C | S, typeKey?: K): string {
const key = typeKey ?? 'type' as K;
if (typeof object === 'string') {
return object;
} else {
if (typeof object === 'function') {
return (object as VariantCreator<string, Func, K>).type;
} else {
return (object as Property<K, string>)[key];
}
}
}
34 changes: 34 additions & 0 deletions src/remote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {match, Handler} from './match';
import {isType} from './tools';
import {Property, Variant, VariantModule, VariantOf} from './variant';


type IsFunctions<T extends VariantModule<K>, K extends string = 'type'> = {
[P in keyof T]: <O extends Property<K, string>>(object: O | {} | null | undefined) => object is VariantOf<T, P, K>;
}
function isFunctions<T extends VariantModule<K>, K extends string = 'type'>(vmod: T) {
const keys = Object.keys(vmod) as Array<string & keyof T>;
return keys.reduce((acc, key) => {
return {
...acc,
[key]: isType(key),
}
}, {}) as IsFunctions<T, K>;
}

export interface Remote<T extends VariantModule<K>, K extends string = 'type'> {
key: K;
is: IsFunctions<T, K>;
new: T;
match: <H extends Handler<T>> (obj: VariantOf<T, undefined, K>, handler: H) => ReturnType<H[keyof H]>
}

export function remote<T extends VariantModule<K>, K extends string = 'type'>(vmod: T, keyProp?: K): Remote<T, K> {
const key = keyProp ?? 'type' as K;
return {
key,
is: isFunctions(vmod),
new: vmod,
match,
}
}
18 changes: 0 additions & 18 deletions src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,6 @@ export function exhaust(x: never, options = {key: 'type', throw: false}) {
}
}

function _set<T, X, Y>(func: (x: X) => Y, data: T) {
// remove T from inputs, add T to outputs
return (input: Identity<Omit<X, keyof T>>) => {
const combined = Object.assign({}, data, input) as T & X;
return func(combined) as Identity<Y & T>;
}
}

type MarkOptional<T, O> = {
[P in keyof T]: P extends keyof O ? (T[P] | undefined) : T[P]
}

function _default<T, X, Y>(func: (x: X) => Y, data: T) {
return (input: MarkOptional<X, T>) => {
const combined = Object.assign({}, data, input) as T & X;
return func(combined) as Y;
}
}

/**
* Handles boilerplate with the most common function definiton.
Expand Down
4 changes: 2 additions & 2 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {VariantModule, Property} from './variant';
/**
* Useful in generating friendly types. Intersections are rendered as the type of the intersection, not as A & B.
*/
export type Identity<T> = {} & {
export type Identity<T> = T extends object ? {} & {
[P in keyof T]: T[P]
};
} : T;

export const identityFunc = <T>(x = {} as T) => x as T extends unknown ? {} : T ;

Expand Down
29 changes: 29 additions & 0 deletions src/variant.order.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {CompareResult, order} from './order';
import {Animal, cerberus} from './__test__/animal';

const rank = order(Animal, [
'dog',
'cat',
'snake',
])

const perseus = rank.new.cat({name: 'Perseus', furnitureDamaged: 0});

test('order new obj', () => {
expect(perseus.name).toBe('Perseus');
expect(perseus.furnitureDamaged).toBe(0);
})

test('order compare', () => {
expect(rank.compare(perseus, cerberus)).toBe(CompareResult.Greater);
})

test('order compare', () => {
expect(rank.compare(Animal.cat, cerberus)).toBe(CompareResult.Greater);
})

test('order index', () => {
expect(rank.index(cerberus)).toBe(0);
expect(rank.index('cat')).toBe(1);
expect(rank.index(Animal.snake)).toBe(2);
})
31 changes: 31 additions & 0 deletions src/variant.remote.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {remote} from '.';
import {just} from './match';
import {Animal, cerberus} from './__test__/animal'

const $Animal = remote(Animal);

test('remote', () => {
expect($Animal.is.cat(cerberus)).toBeFalsy();
expect($Animal.is.dog(cerberus)).toBeTruthy();
});

test('remote is narrows', () => {
const a = cerberus as Animal;
if ($Animal.is.cat(a)) {
const result = a.furnitureDamaged;
// this object doesn't have this type, but I can access it. Narrowing works.
expect(result).toBeUndefined();
} else {
const result = a.name;
expect(result).toBe('Cerberus');
}
})


test('remote match', () => {
const result = $Animal.match(cerberus, {
cat: just(4),
dog: just(5),
snake: just('jo'),
})
})
2 changes: 1 addition & 1 deletion src/variant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export function variantModule<
...acc,
[key]: variant(key, typeof v[key] === 'function' ? v[key] as Func : identityFunc),
};
}, {} as OutVariant<T>);
}, {} as Identity<OutVariant<T>>);
}

export function constrained<
Expand Down

0 comments on commit 2206ad5

Please sign in to comment.