{"id":13548172,"url":"https://github.com/ericelliott/rtype","last_synced_at":"2025-05-16T12:11:20.342Z","repository":{"id":65990210,"uuid":"43607286","full_name":"ericelliott/rtype","owner":"ericelliott","description":"Intuitive structural type notation for JavaScript.","archived":false,"fork":false,"pushed_at":"2019-02-10T03:19:13.000Z","size":120,"stargazers_count":1128,"open_issues_count":16,"forks_count":38,"subscribers_count":42,"default_branch":"master","last_synced_at":"2025-04-02T03:16:42.231Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ericelliott.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-10-03T17:17:40.000Z","updated_at":"2025-03-23T23:03:07.000Z","dependencies_parsed_at":"2023-07-23T02:31:12.680Z","dependency_job_id":null,"html_url":"https://github.com/ericelliott/rtype","commit_stats":{"total_commits":146,"total_committers":9,"mean_commits":16.22222222222222,"dds":0.4246575342465754,"last_synced_commit":"73a58babe99ad03cb9706eab00723f4c0c58b74d"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericelliott%2Frtype","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericelliott%2Frtype/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericelliott%2Frtype/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericelliott%2Frtype/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ericelliott","download_url":"https://codeload.github.com/ericelliott/rtype/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247980836,"owners_count":21027808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T12:01:06.677Z","updated_at":"2025-04-09T05:07:46.980Z","avatar_url":"https://github.com/ericelliott.png","language":"JavaScript","readme":"# Rtype\n\n[![Join the chat at https://gitter.im/ericelliott/rtype](https://badges.gitter.im/ericelliott/rtype.svg)](https://gitter.im/ericelliott/rtype?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\nIntuitive structural type notation for JavaScript.\n\n```js\n(parameterName: Type) =\u003e ReturnType\n```\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n## Table of Contents\n\n- [About Rtype](#about-rtype)\n  - [What is Rtype?](#what-is-rtype)\n  - [Status: RFC](#status-rfc)\n  - [Why?](#why)\n  - [Why Not Just Use TypeScript?](#why-not-just-use-typescript)\n- [Reading Function Signatures](#reading-function-signatures)\n  - [Optional Parameters](#optional-parameters)\n  - [Anonymous Parameters](#anonymous-parameters)\n  - [Type Variables](#type-variables)\n  - [Reserved Types](#reserved-types)\n    - [Builtin Types](#builtin-types)\n    - [The `Any` Type](#the-any-type)\n    - [The `Void` Type](#the-void-type)\n    - [The `Predicate` Type](#the-predicate-type)\n    - [The `Iterable` Type](#the-iterable-type)\n    - [The `TypedArray` Type](#the-typedarray-type)\n  - [Literal Types](#literal-types)\n  - [Tuples](#tuples)\n  - [Union Types](#union-types)\n  - [Negated Types](#negated-types)\n  - [Constructors](#constructors)\n  - [Accessor Descriptors](#accessor-descriptors)\n  - [Throwing Functions](#throwing-functions)\n  - [Dependencies](#dependencies)\n- [Interface: User Defined Types](#interface-user-defined-types)\n  - [Function Interface](#function-interface)\n  - [`this` Binding](#this-binding)\n  - [Dynamic Property Keys](#dynamic-property-keys)\n  - [Predicate Literals](#predicate-literals)\n- [Composing Types](#composing-types)\n- [Event Emitters](#event-emitters)\n- [Comments](#comments)\n- [References](#references)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n\n## About Rtype\n\n* Great for simple documentation.\n* Compiler-agnostic type notation - for use with ECMAScript standard tools.\n* Low learning curve for JavaScript developers.\n* Can embed in JS as strings, for example with [rfx](https://github.com/ericelliott/rfx) for easy runtime reflection.\n* Standing on the shoulders of giants. Inspired by: ES6, TypeScript, Haskell, Flow, \u0026 React\n\n### What is Rtype?\n\nRtype is a JS-native representation of structural type interfaces with a TypeScript-inspired notation that's great for documentation.\n\n\n### Status: RFC\n\nDeveloper preview. [Please comment](https://github.com/ericelliott/rtype/issues/new).\n\nCurrently in-use for production library API documentation, but breaking changes are expected.\n\nIn the future, libraries may parse rtype strings and return predicate functions for runtime type checking. Static analysis tools are also [possible](https://github.com/ericelliott/rtype/issues/89), but no significant developer tooling is currently available. Feel free to build some!\n\n\n### Why?\n\nPerhaps the most important part of API documentation is to quickly grasp the function signatures and data structures required to work with the API. There are existing standards for this stuff, but we think we can improve on them:\n\n* JSDoc is too verbose, not intuitive, and painful to maintain.\n* TypeScript's structural types are very appealing, but opting into TypeScript's JS superset and runtime limitations is not.\n\nWe want a type representation that is very clear to modern JavaScript developers (ES2015+), that could potentially be used at runtime with simple utilities.\n\n\n### Why Not Just Use TypeScript?\n\nWe want the best of all worlds:\n\n* An intuitive way to describe interfaces for the purposes of documentation, particularly function signatures.\n* Runtime accessible type reflection (even in production) with optional runtime type checks that can be disabled in production (like React.PropTypes). See [rfx](https://github.com/ericelliott/rfx#rfx).\n* A way to specify types in standard ES2015+ code. Use any standard JS compiler. See [rfx](https://github.com/ericelliott/rfx#rfx).\n* An easy way to generate interface documentation (like JSDoc).\n\nTypeScript is great for compile-time and IDE features, and you could conceivably generate docs with it, but runtime features are lacking. For example, I want the ability to query function signatures inside the program at runtime, along with the ability to turn runtime type checking on and off. AFAIK, that's not possible with TypeScript (yet - there is experimental runtime support using experimental features of the ESNext `Reflect` API).\n\n\n\n## Reading Function Signatures\n\nFunction types are described by a **function signature**. The function signature tells you each parameter and its type, separated by a colon, and the corresponding return type:\n\n```js\n(param: Type) =\u003e ReturnType\n```\n\nTo make the signature familiar to readers, we use common JavaScript idioms such as destructuring, defaults, and rest parameters:\n\n```js\n(...args: [...String]) =\u003e Any\n({ count = 0: Number }) =\u003e Any\n```\n\nIf a parameter or property has a default value, most built-in types can be inferred:\n\n```js\n({ count = 0 }) =\u003e Any\n```\n\nIf the type is a [union](#union-types) or [`Any`](#the-any-type), it must be specified:\n\n```js\n({ collection = []: Array | Object }) =\u003e Any\n```\n\nOptionally, you may name the return value, similar to named parameters:\n\n```js\n(param: Type) =\u003e name: Type\n```\n\nOr even name a signature to reuse it later on:\n\n```js\nconnect(options: Object) =\u003e connection: Object\n```\n\n### Optional Parameters\n\nOptional parameters can be indicated with `?`:\n\n```js\n(param: Type, optParam?: Type) =\u003e ReturnType\n```\n\n### Anonymous Parameters\n\nParameter names can be omitted:\n\n```js\nis(Any) =\u003e Boolean\n```\n\nIn the case of an anonymous [optional parameter](#optional-parameters) the type must be prefixed by `?:`:\n\n```js\ntoggle(String, ?: Boolean) =\u003e Boolean\n```\n\nIn the case of an anonymous rest parameter, simply omit the name:\n\n```js\n(...: [...Any]) =\u003e Array\n```\n\n### Type Variables\n\nType variables are types that do not need to be declared in advance. They may represent any type, but a single type variable may only represent one type at a time in the scope of the signature being declared.\n\nThe signature for double is usually thought of like this:\n\n```js\ndouble(x: Number) =\u003e Number\n```\n\nBut what if we want it to accept objects as well?\n\n```js\nconst one = {\n  name: 'One',\n  valueOf: () =\u003e 1\n};\n\ndouble(one); // 2\n```\n\nIn that case, we'll need to change the signature to use a type variable:\n\n```js\ndouble(x: n) =\u003e Number\n```\n\nBy convention, type variables are single letters and lowercased in order to visually distinguish them from predefined types. That way the reader doesn't need to scan back through documentation looking for a type declaration where there is no type declaration to be found.\n\n\n### Reserved Types\n\n#### Builtin Types\n\n```js\nArray, Boolean, Function, Number, Object, RegExp, String, Symbol\nArrayBuffer, Date, Error, Map, Promise, Proxy, Set, WeakMap, WeakSet\n```\n\n##### Notes\n\n- `null` is part of `Any` and is *not* covered by `Object`. If you want to allow `null` with `Object`, you must specify the union explicitly: `Object | null`\n- the `Function` builtin type expands to `(...args: [...Any]) =\u003e Any`\n\n#### The `Any` Type\n\nThe special type `Any` means that any type is allowed:\n\n```js\n(...args: [...Any]) =\u003e Array\n```\n\n#### The `Void` Type\n\nThe special type `Void` should only be used to indicate that a function returns no meaningful value (i.e., `undefined`). Since `Void` is the default return type, it can be optionally omitted. Nevertheless `Void` return types *should* usually be explicitly annotated to denote function side-effects.\n\n```js\nset(name: String, value: String) =\u003e Void\n```\n\nIs equivalent to:\n\n```js\nset(name: String, value: String)\n```\n\n#### The `Predicate` Type\n\nThe special type `Predicate` is a function with the following signature:\n\n```js\n(...args: [...Any]) =\u003e Boolean\n```\n\n#### The `Iterable` Type\n\nArrays, typed arrays, strings, maps and sets are iterable. Additionally any object that implements the @@iterator method can be iterated.\n\n```js\n(paramName: Iterable) =\u003e Void\n```\n\nIs equivalent to\n\n```ts\ninterface Iterator {\n  next() =\u003e {\n    done: Boolean,\n    value?: Any\n  }\n}\n\ninterface IterableObject {\n  [Symbol.iterator]: () =\u003e Iterator\n}\n\n(paramName: IterableObject) =\u003e Void\n```\n\n#### The `TypedArray` Type\n\nIt covers these contructors: `Int8Array`, `Uint8Array`, `Uint8ClampedArray`, `Int16Array`, `Uint16Array`, `Int32Array`, `Uint32Array`, `Float32Array`, `Float64Array`.\n\n### Literal Types\n\nLiterals are also accepted as types.\n\n```js\nsignatureName(param1: String, param2: 'value1' | 'value2' | 'value3') =\u003e -1 | 0 | 1\n```\n\n### Tuples\n\nThe type of arrays' elements can also be specified:\n\n```js\n// an array that contains exactly 2 elements\n[Number, String]\n```\n\nFor **∅ or more** and **1 or more** element(s) of the same type you can use the rest operator like so:\n\n```js\n// 0 or more\n[...Number]\n\n// 1 or more\n[Number...]\n//which is equivalent to\n[Number, ...Number]\n```\n\n### Union Types\n\nUnion types are denoted with the pipe symbol, `|`:\n\n```js\n(userInput: String | Number) =\u003e String | Number\n```\n\n### Negated Types\n\nIt is sometime easier and more informative to delimit a type by defining what it's not. The negation operator lets you exclude by substracting from `Any`.\n\n```js\nJSON::parse(String, reviver: Function)\n  =\u003e Boolean | Number | String | Object | Array | null,\n  throws SyntaxError\n\n// is less concise than\n\nJSON::parse(String, reviver: Function)\n  =\u003e !Function \u0026 !Void \u0026 !Symbol,\n  throws SyntaxError\n\n// which is equivalent to\n\nJSON::parse(String, reviver: Function)\n  =\u003e !(Function | Void | Symbol),\n  throws SyntaxError\n```\n\n### Constructors\n\nConstructors in JavaScript require the `new` keyword. You can identify a constructor signature using the `new` keyword as if you were demonstrating usage:\n\n```js\nnew User({ username: String }) =\u003e UserInstance\n```\n\nIn JavaScript, a class or constructor is not synonymous with an interface. The class or constructor definition describe the function signature to create the object instances. A separate signature is needed to describe the instances created by the function. For that, use a separate interface with a different name:\n\n```js\ninterface UserInstance {\n  username: String,\n  credentials: String\n}\n```\n\n### Accessor Descriptors\n\nAn accessor function is defined by prefixing a method with `get` or `set`.\n\n```js\nnew User({ username: String }) =\u003e {\n  username: String,\n  get name() =\u003e String,\n  set name(newName: String) // return type defaults to Void\n}\n```\n\n### Throwing Functions\n\nTo indicate that a function can throw an error you can use the `throws` keyword.\n\n```js\n(paramName: Type) =\u003e Type, throws: TypeError | DOMException\n```\n\nFor the generic `Error` type, you can optionally omit the throw type:\n\n```js\n(paramName: Type) =\u003e Type, throws\n```\n\nIs equivalent to:\n\n```js\n(paramName: Type) =\u003e Type, throws: Error\n```\n\n\n### Dependencies\n\nYou can optionally list your functions' dependencies. In the future, add-on tools may automatically scan your functions and list dependencies for you, which could be useful for documentation and to identify polyfill requirements.\n\n```js\n// one dependency\nsignatureName() =\u003e Type, requires: functionA\n\n// several dependencies\nsignatureName()\n  =\u003e Type,\n  requires: functionA, functionB\n```\n\n\n## Interface: User Defined Types\n\nYou can create your own types using the `interface` keyword.\n\nAn interface can spell out the structure of an object:\n\n```js\ninterface UserProfile {\n  name: String,\n  avatarUrl?: Url,\n  about?: String\n}\n```\n\nInterfaces support builtin literal types:\n\n```js\ninterface UserInstance {\n  name: /\\w+/,\n  description?: '',\n  friends?: [],\n  profile?: {}\n}\n```\n\nA one-line interface doesn't need brackets:\n\n```js\ninterface Name: /\\w+/\n```\n\n### Function Interface\n\nA regular function signature is a shorthand for a function interface:\n\n```js\nuser({ name: String, avatarUrl?: Url }) =\u003e UserInstance\n```\n\nA function interface must have a function signature:\n\n```js\ninterface user {\n  ({ name: String,  avatarUrl?: Url }) =\u003e UserInstance\n}\n```\n\nFor polymorphic functions, use multiple function signatures:\n\n```js\ninterface Collection {\n  (items: [...Array]) =\u003e [...Array],\n  (items: [...Object]) =\u003e [...Object]\n}\n```\n\nIf all signatures return/emit/throw/require the same thing, you can consolidate this information in one place:\n\n```js\ninterface Bar {\n  (String, Object),\n  (String, Boolean)\n} =\u003e Void\n```\n\nNote that named function signatures in an interface block indicate methods, rather than additional function signatures:\n\n```js\ninterface Collection {\n  (signatureParam: Any) =\u003e Any,              // Collection() signature\n  method1(items: [...Array]) =\u003e [...Array],  // method\n  method2(items: [...Object]) =\u003e [...Object] // method\n}\n```\n\nFor convenience you can inline overloaded methods directly inside a function interface.\n\n```js\ninterface Foo {\n  (Type) =\u003e Type,\n\n  a(Object) =\u003e Void,\n  a(String, Number) =\u003e Void,\n\n  b(Object) =\u003e Void,\n  b(String, Number) =\u003e Void\n}\n```\n\nHere is the equivalent using separate interfaces:\n\n```js\ninterface a {\n  (Object) =\u003e Void,\n  (String, Number) =\u003e Void\n}\n\ninterface b {\n  (Object) =\u003e Void,\n  (String, Number) =\u003e Void\n}\n\ninterface Foo {\n  (Type) =\u003e Type,\n\n  a,\n  b\n}\n```\n\n### `this` Binding\n\nSometimes you want to define the shape of the call-site of a function; the `::` operator lets you do just that, _granted_ that you have declared the newly bound interface.  \nFor convenience let's reuse the previously defined [`IterableObject` interface](#the-iterable-type):\n\n```js\n// IterableObject::head() =\u003e Any, throws: TypeError\nconst head = function () {\n  const [first] = this;\n  return first;\n};\n\nhead.call([1,2,3]); // 1\n```\n\n### Dynamic Property Keys\n\nDynamic properties may be labeled and typed. If omitted, the type defaults to `String`.\n\n```js\n{\n  [id1]: {\n    skating: {time: 1000, money: 300},\n    'cooking': {time: 9999, money: 999}\n  },\n  [id2]: {\n    \"jogging\": {time: 300, money: 0}\n  }\n  // etc...\n}\n```\n\nThe preceding object can be expressed using these interfaces:\n\n```ts\ninterface Expenditure {\n  time: Number,\n  money: Number\n}\n\ninterface clientHobbies {\n  [id: Symbol]: {\n    // The following:\n    [hobby]: Expenditure\n    // is equivalent to\n    // [hobby: String]: Expenditure\n  }\n}\n```\n\n### Predicate Literals\n\nInterfaces may use predicate literals, terminated by a semicolon:\n\n```js\ninterface Integer (number) =\u003e number === parseInt(number, 10);\n```\n\nYou can combine predicate literals with interface blocks. Semicolon disambiguates:\n\n```js\ninterface EnhancedInteger (number) =\u003e number === parseInt(number, 10); {\n  isDivisibleBy3() =\u003e Boolean,\n  double() =\u003e Number\n}\n```\n\nMulti-line example:\n\n```js\ninterface EnhancedInteger (number) =\u003e {\n  return number === parseInt(number, 10);\n}; {\n  isDivisibleBy3() =\u003e Boolean,\n  double() =\u003e Number\n}\n```\n\n## Composing Types\n\nWhenever you want to compose an interface out of several others, use the spread operator for that:\n\n```js\ninterface Person {\n  name: Name,\n  birthDate: Number\n}\n\ninterface User {\n  username: String,\n  description?: String,\n  kudos = 0: Number\n}\n\ninterface HumanUser {\n  ...Person,\n  ...User,\n  avatarUrl: String\n}\n```\n\nYou can also use the spread inside object type literals:\n\n```js\ninterface Company {\n  name: Name,\n  owner: { ...Person, shareStake: Number }\n}\n```\n\nIn case of a name conflict, properties with same names are merged. It means all prerequisites must be satisfied. It’s fine to make types more specific through type literals:\n\n```js\ninterface Creature {\n  name: String,\n  character: String,\n  strength: (number) =\u003e (number \u003e= 0 \u0026\u0026 number \u003c= 100)\n}\n\ninterface Human {\n  ...Creature,\n  name: /^(.* )?[A-Z][a-z]+$/,\n  character: 'friendly' | 'grumpy'\n}\n```\n\nTo make sure we can run a static type check for you, we don’t allow merging two different literals. So this would result in a compile error:\n\n```js\n// Invalid!\ninterface Professor {\n  ...Human,\n  name: /^prof\\. \\w+$/\n}\n```\n\nObviously, merging incompatible interfaces is also invalid:\n\n```js\n// Invalid!\ninterface Bot {\n  ...Creature,\n  name: Number\n}\n```\n\n## Event Emitters\n\nWhen composing an observable interface, you can use the `emits` keyword to describe the events it emits:\n\n```js\ninterface Channel {\n  ...EventEmitter\n} emits: {\n  'messageAdded': (body: String, authorId: Number),\n  'memberJoined': (id: Number, { name: String, email: String })\n}\n\n// this is equivalent\n\ninterface Channel {\n  ...EventEmitter\n} emits: {\n  messageAdded(body: String, authorId: Number),\n  memberJoined(id: Number, { name: String, email: String })\n}\n```\n\n## Comments\n\nStandard JS comment syntax applies, e.g.:\n\n```js\n// A single-line comment, can appear at the end of a line.\n\n/*\n  A multi-line comment.\n  Can span many lines.\n*/\n```\n\n\n## References\n\nSomewhat related ideas and inspiration sources.\n\n* [TypeScript](http://www.typescriptlang.org/)\n* [Flow](http://flowtype.org/)\n* [Typed Objects](http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects)\n* [jsig](https://github.com/jsigbiz/spec)\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericelliott%2Frtype","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericelliott%2Frtype","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericelliott%2Frtype/lists"}