Skip to content

feat: add experimental typescript types #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 34 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2aa1432
feat: add experimental typescript types
squirmy Sep 2, 2023
a9ee149
chore: add a few tests for the types
squirmy Sep 3, 2023
87afd60
chore: add linting for ts files
squirmy Sep 3, 2023
03f04a7
chore: extended the test scenarios for the types
squirmy Sep 3, 2023
b625aff
chore: formatting
squirmy Sep 3, 2023
8379790
refactor: human fills bowl
squirmy Sep 3, 2023
a7345c4
feat: export ComposedModule type
squirmy Sep 3, 2023
e1b271e
chore: formatting
squirmy Sep 4, 2023
a99e5b4
feat: omit config from deps if it is passed into compose
squirmy Sep 4, 2023
a380f51
feat: omit config from deps if it is passed into compose
squirmy Sep 4, 2023
ed5089a
refactor: dry config
squirmy Sep 4, 2023
ce8e5e0
chore: formatting
squirmy Sep 4, 2023
8187066
feat: exclude self from deps
squirmy Sep 4, 2023
c7f44f2
feat: use setup composable to compose the module when it exists
squirmy Sep 4, 2023
f5e6b4f
chore: formatting
squirmy Sep 4, 2023
3f14200
Merge remote-tracking branch 'upstream/master'
squirmy Sep 5, 2023
0641229
feat: support modules within modules
squirmy Sep 6, 2023
ab48c8d
feat: add support for deep
squirmy Sep 6, 2023
426850b
feat: allow non-module properties
squirmy Sep 7, 2023
b70e993
refactor: self tests into their own file
squirmy Sep 7, 2023
136914a
refactor: optional deps if the module has no deps
squirmy Sep 7, 2023
bd265b8
refactor: limit depth of compose to 1
squirmy Sep 7, 2023
f89fbf4
refactor: remove exported type
squirmy Sep 7, 2023
82e171a
refactor: simplify type
squirmy Sep 10, 2023
01405cd
fix: correctly detect setup functions
squirmy Oct 13, 2023
77b0fce
feat: expose a type to type modules
squirmy Oct 21, 2023
4276c08
simplify and expose setup module
squirmy Oct 21, 2023
a81ce6d
export ComposedOrSetupModule
squirmy Oct 21, 2023
f53c92a
fix: use object instead of unknown record for config
squirmy Oct 21, 2023
73cb9b5
fix: enforce deps are passed in
squirmy Oct 24, 2023
73425e8
chore: move tests to individual files
squirmy Oct 24, 2023
ba51885
feat: utility types for Inject
squirmy Oct 25, 2023
87c6e7b
fix: omit setup from self
squirmy Oct 25, 2023
82ceab9
feat: support config in deps without specifying it
squirmy Oct 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@
"off"
]
},
"overrides": [],
"overrides": [
{
"files": [
"*.ts"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
]
}
],
"plugins": []
}
126 changes: 126 additions & 0 deletions main.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import type { ConditionalKeys, EmptyObject, Simplify, UnionToIntersection, UnknownRecord } from 'type-fest';

interface CoreOptions {
depth: number
overrides: object
customiser: string
configAlias: string
freezeConfig: boolean
extensions: boolean
}

interface ExtensionOptions {
globalThis: object
publicPrefix: string
privatePrefix: string
functionAlias: object
moduleAlias: string[]
}

type Options = CoreOptions & ExtensionOptions

type ComposerOptionsConfig = object | object[]
interface ComposerOptions {
config?: ComposerOptionsConfig
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Composed = (...args: any[]) => any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type SetupComposable = { setup: (deps: any) => Composed }
type Composable = (deps: Modules) => Composed
type ModuleFunction = Composable | Composed

type Module<T = UnknownRecord> = {
[K in keyof T]: T[K] extends ModuleFunction
? ModuleFunction
: Module<T[K]>
}

type Modules = Record<PropertyKey, Module | unknown>

type ValuesOf<T> = T[keyof T]
type ModuleValuesOf<T> = Extract<ValuesOf<T>, Module>

type AllowedMaxDepth = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
type IncrementDepth<N extends number> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10][N]

type ComposeEach<T extends Module, MaxDepth extends AllowedMaxDepth, N extends number> = {
[K in keyof T]:
N extends MaxDepth
? T[K]
: T[K] extends ModuleFunction
? ReturnType<T[K]>
: ComposeEach<T[K], MaxDepth, IncrementDepth<N>>
}

export type SelfOf<T> = Omit<ComposeEach<ModuleValuesOf<T>, 1, 0>, 'setup'>
export type SelfNameOf<T> = keyof T
export type SpecifiedDepsOf<T, Composed> = Exclude<keyof Composed, SelfNameOf<T>> | never
export type InjectOf<T, Composed, K extends SpecifiedDepsOf<T, Composed>> =
Composed extends { config: unknown }
? Pick<Omit<Composed, SelfNameOf<T>>, | K> & Record<'self' | SelfNameOf<T>, SelfOf<T>> & Record<'config', Composed['config']>
: Pick<Omit<Composed, SelfNameOf<T>>, | K> & Record<'self' | SelfNameOf<T>, SelfOf<T>>

type ComposeType = 'Single' | 'Flat'
export type ComposedModule<T extends Module, CT extends ComposeType = 'Single'> = Simplify<
CT extends 'Single'
? ComposeEach<T, 1, 0>
: ComposeEach<T, 10, 0>
>

type SetupModule<T extends SetupComposable> = ReturnType<ReturnType<T['setup']>>

export type ComposedOrSetupModule<T extends Module, CT extends ComposeType = 'Single'> = Simplify<
T extends SetupComposable
? SetupModule<T>
: ComposedModule<T, CT>
>

type ModuleParameters<T extends Module, Key = keyof T> =
Key extends PropertyKey
? T[Key] extends ModuleFunction
? Parameters<T[Key]>[0]
: ModuleParameters<T[Key]>
: never

type ModuleDependencies<T extends Module, C extends ComposerOptions> =
C['config'] extends ComposerOptionsConfig
? Omit<UnionToIntersection<NonNullable<ModuleParameters<T>>>, 'config'>
: UnionToIntersection<NonNullable<ModuleParameters<T>>>

type Deps<T extends Modules, Path extends keyof T, C extends ComposerOptions> = Omit<ModuleDependencies<ModulePath<T, Path>, C>, Path | 'self'>
type ComposeResult<T extends Modules, Path extends keyof T, CT extends ComposeType> = Record<Path, ComposedOrSetupModule<ModulePath<T, Path>, CT>>

type ModuleKeys<T> = ConditionalKeys<T, Module>
type ModulePath<T, Path extends keyof T> = T extends Module ? T[Path] : never

type Compose<T extends Modules, C extends ComposerOptions> = <Path extends ModuleKeys<T>>(
path: Path,
...args: Deps<T, Path, C> extends EmptyObject ? [Deps<T, Path, C>?, Partial<Options>?] : [Deps<T, Path, C>, Partial<Options>?]
) => ComposeResult<T, Path, 'Single'>

type DeepCompose<T extends Modules, C extends ComposerOptions> = <Path extends ModuleKeys<T>>(
path: Path,
...args: Deps<T, Path, C> extends EmptyObject ? [Deps<T, Path, C>?, Partial<Options>?] : [Deps<T, Path, C>, Partial<Options>?]
) => ComposeResult<T, Path, 'Flat'>

interface Asis<T extends Modules> {
asis<Path extends keyof T>(path: Path, opts?: Partial<Options>): Record<Path, T[Path]>
}

interface Deep<T extends Modules, C extends ComposerOptions> {
deep: DeepCompose<T, C>
}

interface Target<T> {
target: T
}

interface Composer<T extends Modules, C extends ComposerOptions> {
compose: Target<T> & Compose<T, C> & Asis<T> & Deep<T, C>
}

declare function composer<T extends Modules, C extends ComposerOptions>(target: T, config?: C): Composer<T, C>

export default composer;
Loading