From 1d0b40e113dba6505f1de9f1864369de29471c33 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Fri, 26 Apr 2024 19:57:40 +0200 Subject: [PATCH 1/2] docs: improve slow types page --- frontend/docs/about-slow-types.md | 145 ++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 15 deletions(-) diff --git a/frontend/docs/about-slow-types.md b/frontend/docs/about-slow-types.md index bda7f6ef..10b2acb3 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 possible to do in JSR because it would require running the TypeScript +compiler. This is not possible, 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 TypeScript 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; + ``` From ed76336a189638901416c1600640bff8a5f2ee83 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Mon, 29 Apr 2024 12:29:11 +0200 Subject: [PATCH 2/2] review comment --- frontend/docs/about-slow-types.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/docs/about-slow-types.md b/frontend/docs/about-slow-types.md index 10b2acb3..082ba55e 100644 --- a/frontend/docs/about-slow-types.md +++ b/frontend/docs/about-slow-types.md @@ -13,8 +13,8 @@ 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 requires full type checking of the TypeScript source, which is -not possible to do in JSR because it would require running the TypeScript -compiler. This is not possible, because `tsc` does not produce stable type +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)). @@ -264,9 +264,9 @@ 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 TypeScript would perform a type widening or -narrowing operation on the type (for example arrays containing different shapes -of object literals). +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: