├── .gitignore ├── index.d.ts ├── index.js ├── index.ts ├── package.json ├── readme.md ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .history -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { Reverse } from 'typescript-tuple'; 2 | /** 3 | * Extracts function arguments 4 | */ 5 | export declare type ExtractFunctionArguments = Fn extends (...args: infer P) => any ? P : never; 6 | /** 7 | * Extracts function return values 8 | */ 9 | export declare type ExtractFunctionReturnValue = Fn extends (...args: any[]) => infer P ? P : never; 10 | declare type BooleanSwitch = Test extends true ? T : F; 11 | /** 12 | * Replacement for Function, represents any kind of function. 13 | */ 14 | export declare type AnyFunction = (...args: any[]) => any; 15 | /** 16 | * Represents any function with an arity of 1. 17 | */ 18 | export declare type AnyFunction1 = (a: any) => any; 19 | declare type Arbitrary = 'It is now 1554792354 seconds since since Jan 01, 1970'; 20 | declare type IsAny = Arbitrary extends O ? any extends O ? T : F : F; 21 | /** 22 | * A powerful recursive type function composition using `pipe`. 23 | */ 24 | export declare type Pipe = { 25 | 'next': ((..._: Fns) => any) extends ((_: infer First, ..._1: infer Next) => any) ? PreviousFunction extends void ? Pipe, ExtractFunctionReturnValue> : ReturnType extends ExtractFunctionArguments[0] ? Pipe> : IsAny extends true ? Pipe> : { 26 | ERROR: ['Return type ', ReturnType, 'does comply with the input of', ExtractFunctionArguments[0]]; 27 | POSITION: ['Position of problem for input arguments is at', Fns['length'], 'from the', BooleanSwitch, 'and the output of function to the ', BooleanSwitch]; 28 | } : never; 29 | 'done': (...args: InitalParams) => ReturnType; 30 | }[Fns extends [] ? 'done' : 'next']; 31 | /** 32 | * A powerful recursive type function composition using `compose`. 33 | */ 34 | export declare type Compose = Pipe, false>; 35 | /** 36 | * An example of `Pipe` used in a `pipe` function 37 | */ 38 | export declare type PipeFn = (...fns: Fns & Pipe extends AnyFunction ? Fns : never) => Pipe; 39 | /** 40 | * An example of `Pipe` used in a `pipeline` function (a `pipe` that is immediately evaluated) 41 | */ 42 | export declare type PipelineFn = any, ...AnyFunction1[]]>(arg: Arg, ...fns: Fns & Pipe extends AnyFunction ? Fns : never) => ExtractFunctionReturnValue> extends { 43 | ERROR: [string]; 44 | POSITION: [string]; 45 | } ? Pipe : ExtractFunctionReturnValue>; 46 | /** 47 | * An example of `Compose` used in a `compose` function 48 | */ 49 | export declare type ComposeFn = (...fns: Fns & Compose extends AnyFunction ? Fns : never) => Compose; 50 | export {}; 51 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { Reverse } from 'typescript-tuple' 2 | 3 | /** 4 | * Extracts function arguments 5 | */ 6 | export type ExtractFunctionArguments < Fn > = Fn extends ( ...args: infer P ) => any ? P : never 7 | 8 | /** 9 | * Extracts function return values 10 | */ 11 | export type ExtractFunctionReturnValue = Fn extends ( ...args: any[] ) => infer P ? P : never 12 | 13 | type BooleanSwitch = Test extends true ? T : F 14 | 15 | /** 16 | * Replacement for Function, represents any kind of function. 17 | */ 18 | export type AnyFunction = ( ...args: any[] ) => any 19 | 20 | /** 21 | * Represents any function with an arity of 1. 22 | */ 23 | export type AnyFunction1 = ( a: any ) => any 24 | 25 | // This is used as an arbitrary literal that should not match anything else. 26 | type Arbitrary = 'It is now 1554792354 seconds since since Jan 01, 1970' 27 | 28 | // An type to test if a type is `any` within a specific context 29 | type IsAny = Arbitrary extends O 30 | ? any extends O 31 | ? T 32 | : F 33 | : F 34 | 35 | /** 36 | * A powerful recursive type function composition using `pipe`. 37 | */ 38 | export type Pipe = { 39 | 'next': ( ( ..._: Fns ) => any ) extends ( ( _: infer First, ..._1: infer Next ) => any ) 40 | ? PreviousFunction extends void 41 | ? Pipe, ExtractFunctionReturnValue > 42 | : ReturnType extends ExtractFunctionArguments[0] 43 | ? Pipe > 44 | : IsAny extends true 45 | ? Pipe > 46 | : { 47 | ERROR: ['Return type ', ReturnType , 'does comply with the input of', ExtractFunctionArguments[0]], 48 | POSITION: ['Position of problem for input arguments is at', Fns['length'], 'from the', BooleanSwitch , 'and the output of function to the ', BooleanSwitch], 49 | } 50 | : never 51 | 'done': ( ...args: InitalParams ) => ReturnType, 52 | }[ 53 | Fns extends [] 54 | ? 'done' 55 | : 'next' 56 | ] 57 | 58 | /** 59 | * A powerful recursive type function composition using `compose`. 60 | */ 61 | export type Compose = Pipe, false> 62 | 63 | /** 64 | * An example of `Pipe` used in a `pipe` function 65 | */ 66 | export type PipeFn = ( ...fns: Fns & Pipe extends AnyFunction ? Fns : never ) => Pipe 67 | 68 | /** 69 | * An example of `Pipe` used in a `pipeline` function (a `pipe` that is immediately evaluated) 70 | */ 71 | export type PipelineFn = any, ...AnyFunction1[]] >( arg: Arg, ...fns: Fns & Pipe extends AnyFunction ? Fns : never ) => ExtractFunctionReturnValue> extends { ERROR: [string], POSITION: [string]} ? Pipe :ExtractFunctionReturnValue> 72 | 73 | /** 74 | * An example of `Compose` used in a `compose` function 75 | */ 76 | export type ComposeFn = ( ...fns: Fns & Compose extends AnyFunction ? Fns : never ) => Compose 77 | 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pipe-and-compose-types", 3 | "description": "Powerful Typescript types for `pipe` and `compose`", 4 | "version": "0.0.13", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "prepublish": "npm run test && npm run build", 9 | "test": "echo 0", 10 | "dev": "tsc -w", 11 | "build": "tsc" 12 | }, 13 | "devDependencies": { 14 | "tsc": "^1.20150623.0", 15 | "typescript": "^3.4.3" 16 | }, 17 | "dependencies": { 18 | "typescript-tuple": "^2.2.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | This package strictly contains types. The star of this package is a recursive `Pipe` type with surprising utility over parameter overloading. These benefits include 4 | 5 | * parameter name preservation 6 | * theoritically unlimited functions can be composed (recursive) 7 | * variadic input for both `Pipe` and `Compose` 8 | * friendly messages on error, pointing to the problem 9 | 10 | It's biggest weakness is still stronger than the parameter overloading approach, namely generics on compose functions are not preserved; however, it will accept them and the resulting type will be `{}`. The overloading approach can actually often fail to compile in those situations. 11 | 12 | The main benefit of the overloading approach to this recursive type is that when error occur, the compiler is better able to explain where the error occurred. With this type, in the event of a compile error, the returned type will itself contain information on what went wrong. 13 | 14 | # Exports 15 | 16 | 17 | ### Main 18 | Key exported types include 19 | 20 | * `Pipe` as mentioned above 21 | * `Compose` data flows from bottom up ( or left to right ) 22 | * `PipeFn` Pipe applied to create a reference function type 23 | * `ComposeFn` Compose applied to create a reference function type 24 | * `PipelineFn` Pipe applied to create a useful `pipeline` function type 25 | 26 | ### Secondary 27 | 28 | Other types exported 29 | * `ExtractFunctionArguments` extracts function arguments 30 | * `ExtractFunctionReturnValue` extracts a functions return type 31 | 32 | ### Circumstantial 33 | Ad-hoc types that may aid in constructing applications from these types. 34 | * `AnyFunction` a type representing any kind of function, an alternative to Function 35 | * `AnyFunction1` a function representing any function with an arity of 1 36 | 37 | # Installation 38 | 39 | `npm install pipe-and-compose-types` 40 | 41 | # Usage 42 | 43 | Example: 44 | 45 | ```ts 46 | import { PipeFn } from 'pipe-and-compose-types' 47 | 48 | declare const example: ( first: number, rest: number[] ) => number 49 | declare const pipe: PipeFn 50 | 51 | const result = pipe( 52 | example, 53 | String 54 | ) // (first: number, rest: number[]) => string 55 | ``` 56 | 57 | Read more at https://dev.to/babak/introducing-the-recursive-pipe-and-compose-types-3g9o -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | // "lib": [], /* Specify library files to be included in the compilation. */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 12 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 13 | // "outFile": "./", /* Concatenate and emit output to single file. */ 14 | // "outDir": "./", /* Redirect output structure to the directory. */ 15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 16 | // "composite": true, /* Enable project compilation */ 17 | // "incremental": true, /* Enable incremental compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | "strictNullChecks": true, /* Enable strict null checks. */ 29 | "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | 52 | /* Source Map Options */ 53 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 57 | 58 | /* Experimental Options */ 59 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 60 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | tsc@^1.20150623.0: 6 | version "1.20150623.0" 7 | resolved "https://registry.yarnpkg.com/tsc/-/tsc-1.20150623.0.tgz#4ebc3c774e169148cbc768a7342533f082c7a6e5" 8 | integrity sha1-Trw8d04WkUjLx2inNCUz8ILHpuU= 9 | 10 | typescript-compare@^0.0.2: 11 | version "0.0.2" 12 | resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425" 13 | integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA== 14 | dependencies: 15 | typescript-logic "^0.0.0" 16 | 17 | typescript-logic@^0.0.0: 18 | version "0.0.0" 19 | resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196" 20 | integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q== 21 | 22 | typescript-tuple@^2.2.1: 23 | version "2.2.1" 24 | resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2" 25 | integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q== 26 | dependencies: 27 | typescript-compare "^0.0.2" 28 | 29 | typescript@^3.4.3: 30 | version "3.4.3" 31 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.3.tgz#0eb320e4ace9b10eadf5bc6103286b0f8b7c224f" 32 | integrity sha512-FFgHdPt4T/duxx6Ndf7hwgMZZjZpB+U0nMNGVCYPq0rEzWKjEDobm4J6yb3CS7naZ0yURFqdw9Gwc7UOh/P9oQ== 33 | --------------------------------------------------------------------------------