diff --git a/frontend/docs/about-slow-types.md b/frontend/docs/about-slow-types.md index bda7f6ef..082ba55e 100644 --- a/frontend/docs/about-slow-types.md +++ b/frontend/docs/about-slow-types.md @@ -12,11 +12,16 @@ classes, interfaces, or variables, or type aliases, that are themselves or reference "slow types". "Slow types" are types that are not explicitly written, or are so complex that they require extensive inference to be understood. -This inference is too costly to be performed by JSR for these features, so these -kinds of types are not supported in the public API. - +This inference requires full type checking of the TypeScript source, which is +not feasible to do in JSR because it would require running the TypeScript +compiler. It is not feasible because `tsc` does not produce stable type +information over time and is too slow to run on every package in the registry on +a regular basis +([read more in this issue](https://github.com/jsr-io/jsr/issues/444#issuecomment-2079772908)). +Because of this, these kinds of types are not supported in the public API. + + - > :warning: If JSR discovers "slow types" in a package, certain features will > either not work or degrade in quality. These are: > @@ -33,9 +38,9 @@ kinds of types are not supported in the public API. ## What are slow types? -"Slow types" occur when a function, variable, or interface is exported from a -package, and its type is not explicitly written, or is so complex that it cannot -be simply inferred. +"Slow types" occur when a function, class, const declaration, or let declaration +is exported from a package, and its type is not explicitly written or the type +is more complex that what can [be simply inferred](#simple-inference). Some examples of "slow types": @@ -57,17 +62,32 @@ export class MyClass { } ``` -Slow types that are _internal_ to a package (i.e. are not exported) are not -problematic for JSR and consumers of your package. +Slow types that are _internal_ to a package (i.e. are not exported from a file +that is in the `exports` of the package manifest) are not problematic for JSR +and consumers of your package. So, if you have a slow type that is not exported, +you can keep it as is: + +```ts +export add(a: number, b: number): number { + return addInternal(a, b); +} + +// It is ok to not explicitly write the return type of this function, because it +// is not exported (it is internal to the package). +function addInternal(a: number, b: number) { + return a + b; +} +``` ## TypeScript restrictions This section lists out all of the restrictions that the "no slow types" policy imposes on TypeScript code: -1. All exported functions, classes, variables, and types must have explicit - types. For example, functions should have an explicit return type, and - classes should have explicit types for their properties. +1. All exported functions, classes, and variables must have explicit types. For + example, functions should have an explicit return type, and classes should + have explicit types for their properties, and constants should have explicit + type annotations. 1. Module augmentation and global augmentation must not be used. This means that packages cannot use `declare global`, `declare module`, or @@ -112,6 +132,13 @@ Classes should have explicit types for their properties: } ``` +Constants should have explicit type annotations: + +```diff +- export const GLOBAL_ID = crypto.randomUUID(); ++ export const GLOBAL_ID: string = crypto.randomUUID(); +``` + ### Global augmentation Module augmentation and global augmentation must not be used. This means that @@ -151,9 +178,10 @@ Use ESM syntax instead: ### Types must be simply inferred or explicit -All types in exported functions, classes, variables, and types must be simply -inferred or explicit. If an expression is too complex to be inferred, it's type -should be explicitly assigned to an intermediate type. +All types in exported functions, classes, variables, and types must be +[simply inferred](#simple-inference) or explicit. If an expression is too +complex to be inferred, it's type should be explicitly assigned to an +intermediate type. For example, in the following case the type of the default export is too complex to be inferred, so it must be explicitly declared using an intermediate type: @@ -228,3 +256,90 @@ allowed. + type MyPrivateMember = string; ``` + +## Simple inference + +In a few cases, JSR can infer a type without you needing to specify it +explicitly. These cases are called "simple inference". Types that can be simply +inferred are not considered "slow types". + +In general, simple inference is possible if a symbol is does not reference other +symbols. It is also not possible if the TypeScript compiler would perform a type +widening or narrowing operation on the type (for example arrays containing +different shapes of object literals). + +Simple inference is possible in two positions: + +1. The return type of an arrow function, if the function body is a single simple + expression and not a block. +2. The type of a variable (const or let declaration) or property that is + initialized with a simple expression. + +```ts +export const foo = 5; // The type of `foo` is `number`. +``` + +```ts +export const bar = () => 5; // The type of `bar` is `() => number`. +``` + +```ts +class MyClass { + prop = 5; // The type of `prop` is `number`. +} +``` + +This inference can only be performed for a select few simple expressions. These +are: + +1. Number literals. + ```ts + 5; + 1.5; + ``` +2. String literals. + ```ts, + "hello"; + // Template strings are not supported. + ``` +3. Boolean literals. + ```ts + true; + false; + ``` +4. `null` and `undefined`. + ```ts + null; + undefined; + ``` +5. BigInt literals. + ```ts + 5n; + ``` +6. `as T` assertions + ```ts + foo() as MyType; // The type is `MyType`. + ``` +7. `Symbol()` and `Symbol.for()` expressions. + ```ts + Symbol("foo"); + Symbol.for("foo"); + ``` +8. Regular expressions. + ```ts + /foo/; + ``` +9. Array literals with simple expressions as properties (excluding object + literals). + ```ts + [1, 2, 3]; + ``` +10. Object literals with simple expressions as properties. + ```ts + { foo: 5, bar: "hello" }; + ``` +11. Functions or arrow functions that are fully annotated (i.e. have all + parameters and the return type annotated or simply inferred). + ```ts + const x = (a: number, b: number): number => a + b; + ```