Run typescript in browser

Saved searches

Use saved searches to filter your results more quickly

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

Using the TypeScript API in the browser

License

TomasHubelbauer/typescript-in-browser

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

README.md

TypeScript in the Browser

Note: The ability to both transpile and execute TypeScript code in the browser has since been added to the TypeScript Playground: https://www.typescriptlang.org/play

This repository is an experiment in transpiling TypeScript in the browser.

  1. The page loads typescript.js ( typescript/lib/ ) in some way (CDN, node_modules )
  2. The page has the TypeScript and ts objects available on window
  3. The TypeScript API can be used
  4. This demo transpiles TypeScript source text to JavaScript source text based on the demo in the TypeScript wiki
const  outputText, diagnostics, sourceMapText > = ts.transpileModule('const x: number = 5;',  compilerOptions:  target: 'esnext' > >);

Make TSX work in the Monaco editor

Add TS compiler options controls: types, target etc.

Investigate how loading typings could work

About

Using the TypeScript API in the browser

Источник

TypeScript in the Browser

I recently had to get Typescript running in the browser an online code editor that gives you live feedback on your code. We had previously only showed parse and runtime errors, but we wanted to show more advanced type errors that are only detectable with static analysis (e.g. TypeScript).

TypeScript can easily be used programmatically for files on the file system. With a little configuration it can even be run on code that only exists in memory. However, running TypeScript in the browser with no access to the file system turned out to be a bit more challenging than I thought. In this post I will go over setting up a TypeScript language service that you can use to get type errors, quick info, documentation, etc, of code in the browser.

All code in this post can be found in this CodeSandbox.

Credit where credit is due: Andrew’s blog post, Overengineering a blog, and associated source were an invaluable resource. Thanks Andrew!

The main functionality I wanted was to query the compiler for type errors.

function typecheck(code: string): TypeError[] < /* . */ >

For this I reached for the TypeScript language service, which is a layer on top of the core compiler and provides common editor like operations.

TypeScript language service

To typecheck code in the browser we need the following:

  • Virtual system
  • Compiler host
  • Language service host
  • Language service
  • Type files for any language features we want to use

System

A ts.System contains methods for reading files, writing files, checking if a file exists, etc. If you run TypeScript in Node, then the system is a wrapper around fs . The filesystem does not exist in the browser, so we have to create our own virtual system. The full file is here.

// system.ts import * as ts from "typescript"; export function createSystem(files: < [name: string]: string >): ts.System < files = < . files >; return < // . directoryExists: directory =>Object.keys(files).some(path => path.startsWith(directory)), fileExists: fileName => files[fileName] != null, getCurrentDirectory: () => "/", readFile: fileName => files[fileName], >; >

We pass in an object from filename → file contents that contains all of our files on our virtual filesystem.

Library Types

TypeScript knows the types of variables and functions through the use of declaration files ( .d.ts extension). These are what you download when you install a package from DefinitelyTyped ( @types/package ).

Since there are many different versions of TypeScript/JavaScript, the types of core language features are not baked into the compiler. Declaration files for the languages features you want to use must be included in the virtual file system. This is what the lib field in your tsconfig.json file configures.

In this demo we will use es2015 and dom features, equivalent to

The decalaration files are in node_modules and can be loaded at build time using the Webpack raw-loader .

// tsLib.ts export const libs = < "/dom.d.ts": require("!raw-loader!typescript/lib/lib.dom.d.ts"), "/es2015.d.ts": require("!raw-loader!typescript/lib/lib.es2015.d.ts"), "/lib.es5.d.ts": require("!raw-loader!typescript/lib/lib.es5.d.ts"), "/lib.es2015.d.ts": require("!raw-loader!typescript/lib/lib.es2015.d.ts"), "/lib.es2015.core.d.ts": require("!raw-loader!typescript/lib/lib.es2015.core.d.ts"), "/lib.es2015.collection.d.ts": require("!raw-loader!typescript/lib/lib.es2015.collection.d.ts"), "/lib.es2015.generator.d.ts": require("!raw-loader!typescript/lib/lib.es2015.generator.d.ts"), "/lib.es2015.promise.d.ts": require("!raw-loader!typescript/lib/lib.es2015.promise.d.ts"), "/lib.es2015.iterable.d.ts": require("!raw-loader!typescript/lib/lib.es2015.iterable.d.ts"), "/lib.es2015.proxy.d.ts": require("!raw-loader!typescript/lib/lib.es2015.proxy.d.ts"), "/lib.es2015.reflect.d.ts": require("!raw-loader!typescript/lib/lib.es2015.reflect.d.ts"), "/lib.es2015.symbol.d.ts": require("!raw-loader!typescript/lib/lib.es2015.symbol.d.ts"), "/lib.es2015.symbol.wellknown.d.ts": require("!raw-loader!typescript/lib/lib.es2015.symbol.wellknown.d.ts"), >;

If we wanted to use a third party library, we could include the type file here.

Compiler Host

The TypeScript compiler interacts with the host environment via a compiler host.

const compilerHost: ts.CompilerHost = < // . getCanonicalFileName: fileName =>fileName, getDefaultLibFileName: () => "/lib.es2015.d.ts", getDirectories: () => [], getNewLine: () => sys.newLine, getSourceFile: filename => sourceFiles[filename], useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames, >;

The compiler host uses ts.SourceFile s for representing files on the filesystem. These include the content of the file as well as additional metadata, such as the language version. We can easily create source files from an object from filename -> contents.

const sourceFiles: < [name: string]: ts.SourceFile >= <>; for (const name of Object.keys(files))

Language Service Host

The language service host «abstracts all interactions between the language service and the external world. The language service host defers managing, monitoring, and maintaining input files to the host» (source).

This means that the only way the compiler can access the outside world is through the language service host.

const languageServiceHost: ts.LanguageServiceHost = < // . getCompilationSettings: () =>compilerOptions, getScriptFileNames: () => Object.keys(files), getScriptSnapshot: filename => < const contents = sys.readFile(filename); if (contents) < return ts.ScriptSnapshot.fromString(contents); >return undefined; >, >;

In the above code we create a ScriptSnapshot instead of returning the contents of the file as a string because they allow efficient incremental parsing. The snapshot contains information about the entire contents of the file, as well as changes made from a previous snapshot. Since we are not worrying about incremental parsing in this demo, a new snapshot is created every time.

Language Service

The final thing we need is a ts.LanguageService . This will be our main interface into the compiler. In a proper application you would create the language service once and update the (virtual) file contents as you need. This would allow the compiler to perform various optimizations such as only parsing and typechecking code that has changed. However, in our demo we will create a new language service every time we want to typecheck some code.

const languageService = ts.createLanguageService(languageServiceHost);

Getting Diagnostics

The language service contains a bunch of useful functions for querying the compiler.

language service available functions

The one we are most interested in is getSemanticDiagnostics , which will return an array of ts.Diagnostic s that look like,

The field messageText is a human readable description of the problem found.

Implementation

Using the information above, lets implement a function for typechecking some input code.

First, we need to specify some compiler options. This object looks nearly identical to what you find in tsconfig.json .

const compilerOptions: ts.CompilerOptions = < . ts.getDefaultCompilerOptions(), jsx: ts.JsxEmit.React, strict: false, target: ts.ScriptTarget.Latest, esModuleInterop: true, module: ts.ModuleKind.None, suppressOutputPathCheck: true, skipLibCheck: true, skipDefaultLibCheck: true, moduleResolution: ts.ModuleResolutionKind.NodeJs, >;

And now onto our typechecking function. Some code is commented out for concisness as it is shown in more detail above. The full typechecking code can be found here.

const typecheck = (code: string): ts.Diagnostic[] => < // Create our virtual file system const dummyFilename = "file.ts"; const files: < [name: string]: string >= < [dummyFilename]: code, >; const sys = createSystem(< . libs, . files >); // create source files from the `files` object const sourceFiles: < [name: string]: ts.SourceFile >= <>; for (const name of Object.keys(files)) < /* . */ >// create the compiler host, an abstraction for the language service // to interact with the host environment const compilerHost: ts.CompilerHost = < /* . */ >; // create the language service host, an absctraction of all interactions // between the language service and the external world const languageServiceHost: ts.LanguageServiceHost = < /* . */ >; // create an instance of the TypeScript language service const languageService = ts.createLanguageService(languageServiceHost); // get all semantic diagnostics for the code we passed into this function const diagnostics = languageService.getSemanticDiagnostics(dummyFilename); return diagnostics; >;

And there you have it! A simple function for typechecking code in the browser with TypeScript.

Usage

Using the above typecheck function on the code

Improvements

Our current implementation has the overhead of re-creating the language service everytime we call typecheck . An improvment we could make is to only create a single instance of the language service and just update the contents of the virtual file when something changes. If we can track which parts of the file have updated, we can pass that information to the compiler via a ScriptSnapshot to enable incremental parsing.

We could also use the language service to offer IDE like features in the browser with things like quick info, function documentation, JSX closing tag hints, and much more.

Conclusion

Although getting TypeScript running in the browser is not as simple as installing a package from NPM, with a little understaning of how the language service works, we can get the full power of TypeScript in the browser fairly quickly. With that we can query the compiler for type errors and access many more IDE like features.

Resources

Источник

Читайте также:  В питоне есть многопоточность
Оцените статью