JavaScript type hinting & validation (typescript)

Discourse ships type information for its JavaScript code. This can provide inline documentation, autocomplete, and other useful IDE features. It can also be used for some basic type validation, using the @ts-check directive.

Much of this will be automatically consumed by IDEs with TypeScript/JavaScript support. But for functionality in .gjs files, you’ll need some specific configuration and/or IDE plugins.

Writing TypeScript

Core, themes and plugins can be authored directly in TypeScript. Use a .ts extension for plain modules, or .gts for Glimmer components with a <template> tag. Type syntax is stripped at build time, so no separate compilation step is required. Linting (@discourse/lint-configs) and type-checking (pnpm lint:types) both understand these files.

Strict checking

pnpm lint:types runs in TypeScript’s strict mode, and TypeScript in core and in the bundled plugins and themes must pass it. Plugins and themes outside this repository extend the same config, so they are checked under strict too. To opt out, set "strict": false in the compilerOptions of the plugin’s or theme’s own tsconfig.json.

JavaScript files are not type-checked, but the published @discourse/types declarations for them are generated under strict. A field initialized to null is declared with the type null, so give public values like this a JSDoc type such as /** @type {number | null} */.

Type tests

Types whose meaning a runtime test can’t capture (a generic’s resolved type, an overload pick, a return type derived from arguments) can be asserted at compile time with expect-type, in .ts/.gts files under frontend/discourse/type-tests/ (e.g. type-tests/truth-helpers/). They are checked by pnpm lint:types but kept out of the test and production bundles.

Usage

  • CLI: Run pnpm lint:types

  • VSCode: Install the TypeScript 7 and Glint v2 extensions. Glint 1.4.0 and newer hands .gts/.gjs files to TypeScript’s own language server. Both are part of our recommended config, so you may already have it. If anything isn’t working, you may need to trigger “Restart extension host” from VSCode’s command palette, or restart the IDE.

  • JetBrains (RubyMine, WebStorm, Intellij, etc.): Install the EmberExperimental plugin.

Troubleshooting

Ensure that you’ve run pnpm install recently

Enabling for a theme or plugin

Official themes/plugins, and the official skeletons, are all wired up for types. To enable it for your own plugin/theme, pull in the latest changes from the relevant skeleton (package.json, tsconfig.json)

Bundled plugins and themes

Plugins and themes in this repository depend on @discourse/types through the pnpm workspace and reference the core project from their tsconfig.json. pnpm lint:types builds core first, so a core type change is checked against every bundled plugin in the same run, with no release or version bump in between.

In the editor, the TypeScript 7 language server resolves core imports straight to core source. Editors still on TypeScript 6 read the declarations that the last pnpm lint:types emitted, so run it, or keep pnpm types:watch running, after changing core types.

External plugins and themes consume the published @discourse/types package. It is published from main by the publish-types workflow.

Relative imports

Relative imports name the file with its extension: ./foo.ts, ./foo.js, ./foo.gts, ./foo.gjs. TypeScript 7 only resolves a .gts or .gjs module when the specifier carries the extension, and the same rule is applied to every extension so that the type checker, the build and ESLint agree. ESLint reports a missing extension and adds it with --fix.

Enable checking for a file

.ts and .gts files are always type-checked. For .js / .gjs files, type-checking is opt-in: add /** @ts-check */ at the top. For some examples, search Discourse core for @ts-check.

Limitations

We do not provide any guarantees about the accuracy of the types - they’re provided on a best-effort basis. PRs to improve the type documentation in core are welcome.

Known Issues

  • Autocomplete inside <template> tags requires complete syntax. For example, if you start typing:

    <DBu
    

    This will not autocomplete to DButton, because the template syntax cannot be parsed. The workaround is to close the brackets, and then go back to the variable you’d like to autocomplete:

    <DBu />
    

    Upstream issue here


This document is version controlled - suggest changes on github.

10개의 좋아요