Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/char2sgu/type-container
Essential component for TypeScript magics.
https://github.com/char2sgu/type-container
generics type-container type-gymnastics types typescript
Last synced: about 1 month ago
JSON representation
Essential component for TypeScript magics.
- Host: GitHub
- URL: https://github.com/char2sgu/type-container
- Owner: Char2sGu
- License: apache-2.0
- Created: 2023-09-21T17:38:37.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-07-06T13:09:06.000Z (6 months ago)
- Last Synced: 2024-07-06T14:28:22.876Z (6 months ago)
- Topics: generics, type-container, type-gymnastics, types, typescript
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/type-container
- Size: 13.7 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Type Container
Essential component for TypeScript magics.
```sh
npm i type-container
```## Fundamentals
A Type Container is simply an empty object but carries specific TypeScript type information, enabling infinite possibilities of advanced type inferences and manipulations.
The `$type` function produces a type container:
```ts
const $payload = $type<{ id: string }>();
// $payload -> TypeContainer<{ id: string }>
```> Conventionally, variables and parameters of a `TypeContainer` type are prefixed with `$` for easy identification.
To extract the type information from a type container, use the `ContainedTypeOf` type:
```ts
const $payload = $type<{ id: string }>();
type Payload = ContainedTypeOf;
// Payload -> { id: string }
```## Practical Cases
A typical use case of type containers is to enable generic-type inference from function parameters that are not actually used within the function.
Let's say we have a `createActionFactory()` function:
```ts
declare function createActionFactory<
Name extends string,
Payload extends object
>(name: Name): ActionFactory;interface ActionFactory {}
```We have two generic types: `Name` and `Payload`, but only `Name` is referenced in the function's parameters, and thus only `Name` can be automatically inferred:
```ts
const factory = createActionFactory("FetchUser");
// factory -> ActionFactory<"FetchUser", object>
```An ugly workaround is to get rid of type inference and explicitly specify all the generic types:
```ts
const factory = createActionFactory<"FetchUser", { id: string }>("FetchUser");
// factory -> ActionFactory<"FetchUser", { id: string; }>
```However, the string literal `'FetchBook'` would have to be repeated because of the lack of generic inference. When the generics are complicated, it could be impossible to manually supply all the generic, and that's when Type Containers come handy.
Let's update the function's signature to include a second parameter of type `TypeContainer` for only type inference purposes:
```ts
declare function createActionFactory<
Name extends string,
Payload extends object
>(
name: Name,
// 👇 TypeContainer parameter for type inference purposes only
$payload: TypeContainer
): ActionFactory;
```Since all the generic types are involved in the parameters, the full power of TypeScript's type inference can be unleashed:
```ts
const factory = createActionFactory("FetchUser", $type<{ id: string }>());
// factory -> ActionFactory<"FetchUser", { id: string }>
// generic Name is inferred as "FetchBook"
// generic Payload is inferred as { id: string }
```## Library Integration
If you would like to make use of `type-container` in your libraries, it is recommended to list `type-container` as a peer dependency of your library:
```json
{
"peerDependencies": {
"type-container": "..."
},
"devDependencies": {
"type-container": "..."
}
}
```Alternatively, you could re-export all the members of `type-container` in your own library's entry file for a more invisible integration:
```ts
export * from "type-container";
```