# TypeScript Goodies
### AnyObject: [playground](https://t.ly/AWkS)
```typescript
/**
* Use this if you want to type "any object".
*
* @example
* declare type AnyObject = Record<string, any>
*
* type ExpectsObject<T extends {}> = T
* type BetterExpectsObject<T extends AnyObject> = T
*
* type Test1 = ExpectsObject<2> // this should not compile
* type Test2 = BetterExpectsObject<2> // this throws an error like expected
*/
declare type AnyObject = Record<string, any>;
```
### AnyValue
```typescript
/**
* Use this if you want a type meaning "any value".
*/
declare type AnyValue = unknown;
```
### BetterArray: [playground](http://ibit.ly/XWea)
```typescript
/**
* `BetterArray` is capable of typing and compiling the four
* `.reduce` calls below, while the base `Array` definitions will
* throw errors or return incorrect types.
*
* @example
* declare const array: BetterArray<number>
*
* array.reduce<string>((prev, current) => `${prev} ${current}`)
* array.reduce((prev: number|string, current) => `${prev} ${current}`)
*
* array.reduce<number[]>((prev, current) => [
* ...(Array.isArray(prev) ? prev : [prev]),
* current,
* ]);
*
* declare const flatten: BetterArray<number | number[]>
*
* flatten.reduce((prev: number | number[], curr) => [
* ...(Array.isArray(prev) ? prev : [prev]),
* ...(Array.isArray(curr) ? curr : [curr]),
* ])
*/
interface BetterArray<T> {
reduce<R>(callback: (prev: T | T extends (infer V)[] ? V : T, curr: T, currIndex: number, array: T[]) => R): R
reduce(callback: (prev: T, curr: T, currIndex: number, array: T[]) => T, initial: T): T
reduce<U>(callback: (prev: U, curr: T, currIndex: number, array: T[]) => U, initial: U): U
}
```
### Discriminate: [playground](https://t.ly/xv4j)
```typescript
/**
* Create a discriminated union between the types in T. Either uses existing
* `_type` field or generates a `_type` field based off the index of the type
* in `Types`.
*
* @example
* declare const test: Discriminate<[
* { apple: number },
* { banana: string },
* { carrot: boolean },
* ]>
*
* if (test._type === '0')
* console.log(test.apple)
* else if (test._type === '1')
* console.log(test.banana)
* else // test._type === '2'
* console.log(test.carrot)
*/
declare type Discriminate<T extends [...any[]], Tracker extends any[] = []> =
T extends [infer A, ...infer L]
? A extends AnyObject
? | (
A extends { _type: unknown }
? A
: {
[k in keyof A | '_type']: k extends '_type'
? `${Tracker['length']}`
: A[k]
}
)
| Discriminate<[...L], [A, ...Tracker]>
: never
: never
```
### EmptyObject: [playground](http://t.ly/6Cdp)
```typescript
/**
* Use this if you want a type meaning "empty object".
*
* @example
* declare type EmptyObject = Record<string, never>;
*
* type ExpectEmpty<T extends {}> = any
* type BetterExpectEmpty<T extends EmptyObject> = any
*
* type Test1 = ExpectEmpty<{ foo: boolean }>; // this should throw, but won't
* type Test2 = BetterExpectEmpty<{ foo: boolean }>; // this correctly throws
*/
declare type EmptyObject = Record<string, never>;
```
### MakeAllRequired: [playground](http://t.ly/SP60)
```typescript
/**
* Make all properties in T required
* @example
* type RequiredFoo = MakeAllRequired<{ foo?: string | null; bar?: number; }>;
*
* declare const test: RequiredFoo
* test.foo // string
* test.bar // number
*/
declare type MakeAllRequired<T extends AnyObject> = MakeRequired<T, keyof T>;
```
### MakeOptional: [playground](http://t.ly/5x37)
```typescript
/**
* Make all properties which are assignable to K in T optional.
*
* @example
* type OptionalFoo = MakeOptional<{ foo: string; bar: boolean }, 'foo'>;
* declare const test: OptionalFoo; // { foo?: string; bar: boolean }
*/
declare type MakeOptional<T, K extends keyof T> =
Omit<T, K> & { [key in K]+?: Exclude<T[key], null | undefined> };
```
### MakeRequired: [playground](http://t.ly/qese)
```typescript
/**
* Make all properties which are assignable to K in T required.
*
* @example
* type RequiredFoo = MakeRequired<{foo?: string | null}, 'foo'>
*
* declare const test: RequiredFoo
* test.foo // string
*/
declare type MakeRequired<T, K extends keyof T> =
Omit<T, K> & { [key in K]-?: Exclude<T[key], null | undefined> };
```
### OmitStrict: [playground](http://t.ly/xIBM)
```typescript
/**
* Omit with strict bounds on the keys that can be passed, they must extend
* keyof T to be valid. Note: this is not ALWAYS useful, especially when making
* types that are generic. The "loose"-typed `Omit` still has a purpose.
*
* @example
* declare type OmitStrict<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
*
* type Loose = Omit<{ foo: string }, 'baz'>
* type Strict = OmitStrict<{ foo: string }, 'baz'> // TS error
*/
declare type OmitStrict<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
```
### Replace: [playground](http://t.ly/UV2d)
```typescript
/**
* Replace property Target in T with Replacement
* @example
* type Example = Replace<{foo: string}, 'foo', number>
*
* declare const test: Example
* test.foo // number
*/
declare type Replace<T, Target extends keyof T, Replacement> =
Omit<T, Target> & { [key in Target]: Replacement };
```
### Unbox: [playground](https://t.ly/zLip)
```typescript
/**
* Unboxes values from promises, functions, arrays and arrays recursively.
*
* Will stop at `limit` recursions. If `0` is provided will recurse until there
* is nothing left to unbox.
*
* @example
* // Basic examples
* Unbox<()=>number> // number
* Unbox<string> // string
* Unbox<boolean[]> // boolean
* Unbox<Promise<boolean>> // boolean
* Unbox<() => Promise<() => Array<Promise<boolean>>>> // boolean
*
* // Recursive examples
* Unbox<() => () => () => () => number> // number
* Unbox<() => () => () => () => number, 3> // () => number
* Unbox<() => Promise<() => Array<Promise<boolean>>>> // boolean
*/
declare type Unbox<T, Level extends number = 0, Visited extends number[] = [1]> =
T extends Promise<infer V> ? ContinueUnboxing<V, Level, Visited> :
T extends (infer V)[] ? ContinueUnboxing<V, Level, Visited> :
T extends (...args: unknown[]) => infer V ? ContinueUnboxing<V, Level, Visited> :
T
type ContinueUnboxing<T, L extends number, V extends number[]> =
L[] extends never[]
? Unbox<T, L>
: L extends V['length'] ? T : Unbox<T, L, [...V, 1]>
```