Skip to content

Commit

Permalink
Merge pull request #88 from andnp/ExtendsUnknown
Browse files Browse the repository at this point in the history
fix: head off breaking change in ts@next
  • Loading branch information
andnp authored Mar 12, 2019
2 parents 222befe + d95b27d commit 2e56c72
Show file tree
Hide file tree
Showing 15 changed files with 53 additions and 149 deletions.
44 changes: 5 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ npm install --save-dev simplytyped

**[Objects](#objects)**

[AllKeys](#allkeys) - [AllRequired](#allrequired) - [CombineObjects](#combineobjects) - [DeepPartial](#deeppartial) - [DeepReadonly](#deepreadonly) - [DiffKeys](#diffkeys) - [GetKey](#getkey) - [HasKey](#haskey) - [Intersect](#intersect) - [Keys](#keys) - [KeysByType](#keysbytype) - [Merge](#merge) - [ObjectKeys](#objectkeys) - [ObjectType](#objecttype) - [Omit](#omit) - [Optional](#optional) - [Overwrite](#overwrite) - [PlainObject](#plainobject) - [PureKeys](#purekeys) - [Required](#required) - [SharedKeys](#sharedkeys) - [StrictUnion](#strictunion) - [StringKeys](#stringkeys) - [TaggedObject](#taggedobject) - [UnionizeProperties](#unionizeproperties) - [UnionKeys](#unionkeys)
[AllKeys](#allkeys) - [AllRequired](#allrequired) - [CombineObjects](#combineobjects) - [DeepPartial](#deeppartial) - [DeepReadonly](#deepreadonly) - [DiffKeys](#diffkeys) - [GetKey](#getkey) - [HasKey](#haskey) - [Intersect](#intersect) - [KeysByType](#keysbytype) - [Merge](#merge) - [ObjectKeys](#objectkeys) - [ObjectType](#objecttype) - [Omit](#omit) - [Optional](#optional) - [Overwrite](#overwrite) - [PlainObject](#plainobject) - [PureKeys](#purekeys) - [Required](#required) - [SharedKeys](#sharedkeys) - [StrictUnion](#strictunion) - [StringKeys](#stringkeys) - [TaggedObject](#taggedobject) - [UnionizeProperties](#unionizeproperties) - [UnionKeys](#unionkeys)

**[Utils](#utils)**

[NoInfer](#noinfer) - [Nominal](#nominal) - [Nullable](#nullable) - [PromiseOr](#promiseor) - [Unknown](#unknown)
[NoInfer](#noinfer) - [Nominal](#nominal) - [Nullable](#nullable) - [PromiseOr](#promiseor)

**[Functions](#functions)**

[AnyFunc](#anyfunc) - [ArgsAsTuple](#argsastuple) - [ConstructorFunction](#constructorfunction) - [OverwriteReturn](#overwritereturn) - [Predicate](#predicate)

**[Strings](#strings)**

[Diff](#diff) - [DropString](#dropstring) - [StringEqual](#stringequal) - [UnionContains](#unioncontains)
[DropString](#dropstring) - [StringEqual](#stringequal) - [UnionContains](#unioncontains)

**[Tuples](#tuples)**

Expand Down Expand Up @@ -221,7 +221,7 @@ test('Can make an object with functions readonly', t => {

### DiffKeys
Gets all of the keys that are different between two objects.
This is a set difference between `Keys<T>` and `Keys<U>`.
This is a set difference between `keyof T` and `keyof U`.
Note that calling this with arguments reversed will have different results.
```ts
test('Can get all keys that are different between objects', t => {
Expand Down Expand Up @@ -283,20 +283,6 @@ test('Can get an object with only shared properties', t => {

```

### Keys
No different than `keyof`, but can look a bit nicer when nesting many types deep.
```ts
test('Can get keys from object', t => {
type obj = { x: number, y: string, z: boolean };
type expected = 'x' | 'y' | 'z';
type got = Keys<obj>;

assert<got, expected>(t);
assert<expected, got>(t);
});

```

### KeysByType
Gets all keys that point to a given type.
```ts
Expand Down Expand Up @@ -595,10 +581,6 @@ test('Will give back a promise containing given type union the type itself', t =

```

### Unknown
A constant type that mimics an unknowable type.


## Functions

### AnyFunc
Expand Down Expand Up @@ -627,7 +609,7 @@ test("Can get a tuple of function's argument types", t => {
type F2 = (x: number, y: string) => any;
type F3 = (x: number, y: string, z: boolean) => any;

type E0 = void[];
type E0 = [];
type E1 = [number];
type E2 = [number, string];
type E3 = [number, string, boolean];
Expand Down Expand Up @@ -691,22 +673,6 @@ test('Can build a predicate function with unknown argument types', t => {

## Strings

### Diff

```ts
test('Can get difference between unions of strings', t => {
type a = 'hi' | 'there';
type b = 'hi' | 'my' | 'friend';

type gotA = Diff<a, b>;
type gotB = Diff<b, a>;

assert<gotA, 'there'>(t);
assert<gotB, 'my' | 'friend'>(t);
});

```

### DropString

```ts
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"commitlint": "^7.0.0",
"husky": "^1.0.0",
"ts-node": "^8.0.3",
"tslint": "^5.8.0"
"tslint": "^5.13.0"
},
"ava": {
"files": [
Expand Down
3 changes: 1 addition & 2 deletions scripts/testTsVersions.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
set -e

# skip ts 2.9 because of bug with skipLibCheck
for v in 2.8.4 3.0.3 3.1.6 3.2.2 next; do
for v in 3.0.3 3.1.6 3.2.2 next; do
npm install --no-save typescript@$v
npm test
done
6 changes: 3 additions & 3 deletions src/impl/objects.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DeepReadonly, Keys, TaggedObject, ObjectKeys } from '../types/objects';
import { DeepReadonly, TaggedObject } from '../types/objects';

/**
* Type guard for any key, `k`.
Expand All @@ -17,7 +17,7 @@ export function isKeyOf<T extends object>(obj: T, k: keyof any): k is keyof T {
* @returns an array of keys from `obj`
*/
export function objectKeys<T extends object>(obj: T) {
return Object.keys(obj) as Array<Keys<T>>;
return Object.keys(obj) as Array<keyof T>;
}

/**
Expand All @@ -34,7 +34,7 @@ export function Readonly<T extends object>(obj: T): DeepReadonly<T> { return obj
* @param key the name of the "tag" parameter
* @returns `obj` with the inner objects tagged with parameter `key` and the key pointing to that inner object
*/
export function taggedObject<T extends Record<ObjectKeys, object>, K extends string>(obj: T, key: K): TaggedObject<T, K> {
export function taggedObject<T extends Record<keyof any, object>, K extends string>(obj: T, key: K): TaggedObject<T, K> {
const keys = objectKeys(obj);
return keys.reduce((collection: any, k) => {
const inner: any = obj[k];
Expand Down
24 changes: 4 additions & 20 deletions src/types/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,13 @@ export type AnyFunc<R = any> = (...args: any[]) => R;
* @param R the new return value of the function
* @returns the function `F` with new return value `R`
*/
export type OverwriteReturn<F extends AnyFunc, R> =
F extends () => any ? () => R :
F extends (x1: infer X1) => any ? (x1: X1) => R :
F extends (x1: infer X1, x2: infer X2) => any ? (x1: X1, x2: X2) => R :
F extends (x1: infer X1, x2: infer X2, x3: infer X3) => any ? (x1: X1, x2: X2, x3: X3) => R :
F extends (x1: infer X1, x2: infer X2, x3: infer X3, x4: infer X4) => any ? (x1: X1, x2: X2, x3: X3, x4: X4) => R :
F extends (x1: infer X1, x2: infer X2, x3: infer X3, x4: infer X4, x5: infer X5) => any ? (x1: X1, x2: X2, x3: X3, x4: X4, x5: X5) => R :
F extends (x1: infer X1, x2: infer X2, x3: infer X3, x4: infer X4, x5: infer X5, x6: infer X6) => any ? (x1: X1, x2: X2, x3: X3, x4: X4, x5: X5, x6: X6) => R :
F extends (x1: infer X1, x2: infer X2, x3: infer X3, x4: infer X4, x5: infer X5, x6: infer X6, x7: infer X7) => any ? (x1: X1, x2: X2, x3: X3, x4: X4, x5: X5, x6: X6, x7: X7) => R :
AnyFunc<R>;
export type OverwriteReturn<F extends Function, R> =
F extends ((...x: infer T) => unknown) ? ((...x: T) => R) : never;

/**
* Returns a tuple type of a functions arguments up to 7.
* @param F a function with up to 7 arguments
* @returns a tuple containing `F`'s argument types
*/
export type ArgsAsTuple<F extends AnyFunc> =
F extends () => any ? void[] :
F extends (x1: infer X1) => any ? [X1] :
F extends (x1: infer X1, x2: infer X2) => any ? [X1, X2] :
F extends (x1: infer X1, x2: infer X2, x3: infer X3) => any ? [X1, X2, X3] :
F extends (x1: infer X1, x2: infer X2, x3: infer X3, x4: infer X4) => any ? [X1, X2, X3, X4] :
F extends (x1: infer X1, x2: infer X2, x3: infer X3, x4: infer X4, x5: infer X5) => any ? [X1, X2, X3, X4, X5] :
F extends (x1: infer X1, x2: infer X2, x3: infer X3, x4: infer X4, x5: infer X5, x6: infer X6) => any ? [X1, X2, X3, X4, X5, X6] :
F extends (x1: infer X1, x2: infer X2, x3: infer X3, x4: infer X4, x5: infer X5, x6: infer X6, x7: infer X7) => any ? [X1, X2, X3, X4, X5, X6, X7] :
any[];
export type ArgsAsTuple<F extends Function> =
F extends ((...x: infer T) => unknown) ? T : never;
54 changes: 23 additions & 31 deletions src/types/objects.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Diff } from './strings';
import { False, True } from './conditionals';
import { AnyFunc } from './functions';

// -------
// Helpers
Expand All @@ -16,7 +14,7 @@ export type PlainObject = Record<string, any>;
* @returns An object formed by the key, value pairs of T
*/
export type ObjectType<T> = {
[k in Keys<T>]: T[k];
[k in keyof T]: T[k];
};
/**
* Takes two objects and returns their intersection.
Expand All @@ -35,7 +33,7 @@ export type CombineObjects<T extends object, U extends object> = ObjectType<T &
* @param K Key to query object for value
* @returns `T[K]` if the key exists, `never` otherwise
*/
export type GetKey<T, K extends keyof any> = K extends Keys<T> ? T[K] : never;
export type GetKey<T, K extends keyof any> = K extends keyof T ? T[K] : never;

// ----
// Keys
Expand All @@ -56,51 +54,45 @@ export type ObjectKeys = keyof any;
* @param T type from which to get keys
* @returns keys of `T` that extend `string`
*/
export type StringKeys<T> = Exclude<Keys<T>, number | symbol>;
/**
* No different than `keyof`, but can look a bit nicer when nesting many types deep.
* @param T type from which to get keys
* @returns keys of `T` that extend `string | number | symbol`
*/
export type Keys<T> = keyof T;
export type StringKeys<T> = Exclude<keyof T, number | symbol>;
/**
* When an object has optional or readonly keys, that information is contained within the key.
* When using optional/readonly keys in another object, they will retain optional/readonly status.
* `PureKeys` will remove the optional/readonly status modifiers from keys.
* @param T type from which to get keys
* @returns keys of `T` without status modifiers (readonly/optional)
*/
export type PureKeys<T> = Record<Keys<T>, Keys<T>>[Keys<T>];
export type PureKeys<T> = Record<keyof T, keyof T>[keyof T];
/**
* Gets all of the keys that are shared between two objects.
* @param T first type from which keys will be pulled
* @param U second type from which keys will be pulled
* @returns the keys that both `T` and `U` have in common.
*/
export type SharedKeys<T, U> = Keys<T> & Keys<U>;
export type SharedKeys<T, U> = keyof T & keyof U;
/**
* Gets all keys between two objects.
* @param T first type from which keys will be pulled
* @param U second type from which keys will be pulled
* @returns the keys of `T` in addition to the keys of `U`
*/
export type AllKeys<T, U> = Keys<T> | Keys<U>;
export type AllKeys<T, U> = keyof T | keyof U;
/**
* Gets all of the keys that are different between two objects.
* This is a set difference between `Keys<T>` and `Keys<U>`.
* This is a set difference between `keyof T` and `keyof U`.
* Note that calling this with arguments reversed will have different results.
* @param T first type from which keys will be pulled
* @param U second type from which keys will be pulled
* @returns keys of `T` minus the keys of `U`
*/
export type DiffKeys<T, U> = Diff<Keys<T>, Keys<U>>;
export type DiffKeys<T, U> = Exclude<keyof T, keyof U>;
/**
* Returns `True` if a key, `K`, is present in a type, `T`, else `False`.
* @param T type to check for existence of key `K`.
* @param K key to query `T` for
* @returns `True` if `K` is a key of `T`. Else `False`.
*/
export type HasKey<T, K extends keyof any> = K extends Keys<T> ? True : False;
export type HasKey<T, K extends keyof any> = K extends keyof T ? True : False;

/**
* @param T the union to get the keys of
Expand All @@ -109,7 +101,7 @@ export type HasKey<T, K extends keyof any> = K extends Keys<T> ? True : False;
export type UnionKeys<T>
// Using a conditional here, so that it distributes over members of the union
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types
= T extends any ? keyof T : never;
= T extends unknown ? keyof T : never;

// -------------
// Manipulations
Expand All @@ -119,15 +111,15 @@ export type UnionKeys<T>
* @param T the object whose property values will be unionized
* @returns a union of the right-side values of `T`
*/
export type UnionizeProperties<T extends object> = T[Keys<T>];
export type UnionizeProperties<T extends object> = T[keyof T];
/**
* Gives back an object with listed keys removed.
* This is the opposite of `Pick`.
* @param T the object whose properties will be removed
* @param K the union of keys to remove from `T`
* @returns `T` with the keys `K` removed
*/
export type Omit<T extends object, K extends Keys<T>> = Pick<T, Diff<Keys<T>, K>>;
export type Omit<T extends object, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
/**
* Returns only the shared properties between two objects.
* All shared properties must be the same type.
Expand Down Expand Up @@ -161,7 +153,7 @@ export type Merge<T extends object, U extends object> = Overwrite<T, U> & U;
* @returns a record where each key of the record is now the `Key` property of the inner object
*/
export type TaggedObject<T extends Record<keyof any, object>, Key extends keyof any> = {
[K in Keys<T>]: T[K] & Record<Key, K>;
[K in keyof T]: T[K] & Record<Key, K>;
};

// ---------
Expand All @@ -174,9 +166,9 @@ export type TaggedObject<T extends Record<keyof any, object>, Key extends keyof
* @returns `Partial<T>` recursively through all properties of `T`
*/
export type DeepPartial<T> = Partial<{
[k in Keys<T>]:
T[k] extends any[] ? Array<DeepPartial<T[k][number]>> :
T[k] extends AnyFunc ? T[k] :
[k in keyof T]:
T[k] extends unknown[] ? Array<DeepPartial<T[k][number]>> :
T[k] extends Function ? T[k] :
T[k] extends object ? DeepPartial<T[k]> :
T[k];
}>;
Expand All @@ -186,15 +178,15 @@ export type DeepPartial<T> = Partial<{
* @returns `T` with all fields marked required
*/
export type AllRequired<T extends object> = {
[K in Keys<T>]-?: NonNullable<T[K]>
[K in keyof T]-?: NonNullable<T[K]>
};
/**
* Mark specific keys, `K`, of `T` as required.
* @param T object whose keys will be marked required
* @param K keys of `T` that will be marked required
* @returns `T` with keys, `K`, marked as required
*/
export type Required<T extends object, K extends Keys<T>> = CombineObjects<
export type Required<T extends object, K extends keyof T> = CombineObjects<
{[k in K]-?: NonNullable<T[k]> },
Omit<T, K>
>;
Expand All @@ -204,7 +196,7 @@ export type Required<T extends object, K extends Keys<T>> = CombineObjects<
* @param K keys of `T` that will be marked optional
* @returns `T` with keys, `K`, marked as optional
*/
export type Optional<T extends object, K extends Keys<T>> = CombineObjects<
export type Optional<T extends object, K extends keyof T> = CombineObjects<
{[k in K]?: T[k] | undefined },
Omit<T, K>
>;
Expand All @@ -214,9 +206,9 @@ export type Optional<T extends object, K extends Keys<T>> = CombineObjects<
* @returns `T` with all keys recursively marked as readonly
*/
export type DeepReadonly<T> = Readonly<{
[k in Keys<T>]:
T[k] extends any[] ? ReadonlyArray<DeepReadonly<T[k][number]>> :
T[k] extends AnyFunc ? T[k] :
[k in keyof T]:
T[k] extends unknown[] ? ReadonlyArray<DeepReadonly<T[k][number]>> :
T[k] extends Function ? T[k] :
T[k] extends object ? DeepReadonly<T[k]> :
T[k];
}>;
Expand Down Expand Up @@ -261,6 +253,6 @@ export type StrictUnion<T> = _StrictUnionHelper<T, T>;
// to refer to each individual member of the union
/** no-doc */
export type _StrictUnionHelper<UnionMember, Union> =
UnionMember extends any ?
UnionMember extends unknown ?
UnionMember & Partial<Record<Exclude<UnionKeys<Union>, keyof UnionMember>, never>>
: never;
20 changes: 9 additions & 11 deletions src/types/predicates.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
import { False, True, And, Or, Not } from './conditionals';
import { Keys } from './objects';
import { AnyFunc } from './functions';

/** no-doc */
export type KnownProblemPrototypeKeys = 'toString' | 'toLocaleString' | 'hasOwnProperty' | 'isPrototypeOf' | 'propertyIsEnumerable' | 'constructor' | 'valueOf';
/** no-doc */
export type ArrayPrototypeKeys = Keys<any[]>;
export type ArrayPrototypeKeys = keyof unknown[];
/** no-doc */
export type NumberPrototypeKeys = Keys<number>;
export type NumberPrototypeKeys = keyof number;
/** no-doc */
export type BooleanPrototypeKeys = Keys<false>;
export type BooleanPrototypeKeys = keyof false;
/** no-doc */
export type StringPrototypeKeys = Keys<string>;
export type StringPrototypeKeys = keyof string;
/** no-doc */
export type ObjectPrototypeKeys = Keys<Object>; // tslint:disable-line
export type ObjectPrototypeKeys = keyof Object;
/** no-doc */
export type FunctionPrototypeKeys = Keys<Function>; // tslint:disable-line
export type FunctionPrototypeKeys = keyof Function;

export type IsNever<S extends string> = Not<(Record<S, True> & Record<string, False>)[S]>;
export type IsType<T, X> = X extends T ? True : False;
export type IsArray<T> = T extends any[] ? True : False;
export type IsArray<T> = T extends unknown[] ? True : False;
export type IsNumber<T> = T extends number ? True : False;
export type IsString<T> = T extends string ? True : False;
export type IsFunction<T> =
Or<
T extends AnyFunc ? True : False,
T extends Function ? True : False>; // tslint:disable-line
T extends Function ? True : False,
T extends Function ? True : False>;

export type IsStringFunction<T extends string> = And<IsString<T>, IsNever<T>>;
export type IsBoolean<T> = T extends boolean ? True : False;
Expand Down
Loading

0 comments on commit 2e56c72

Please sign in to comment.