Hello! 👋
I'm very excited about this project, but I have some questions on how it interacts with TypeScript. Specifically: how it interacts with typdefs from DefinitelyTyped.
Since DT typedefs are based on looking up the package name, are the /web_modules routes used by @pika/web incompatible with DT?:
// For this example, assume the typedef for Preact is coming from DT.
// If so, the typedef will only get applied if our import is from `preact`,
// not from `/web_modules/preact.js`
// This will have typings
import { createElement, Component } from "preact";
// This will not
import { createElement, Component } from "/web_modules/preact.js";
Thanks for your time,
Alex
I think, unless it creates intermediary proxy .d.ts files, this will never work as expected. for example, when pika goes do his thing, it would have to generate a proxy-packages/preact.d.ts with the contents:
```ts
declare module 'web_modules/preact.js' {
import preact from 'preact';
export = preact;
}
````
the problem with this is that it doesn't allow it to be "/web_modules/preact.js" since TS gives "Ambient module declaration cannot specify relative module name", and also needs allowSyntheticDefaultimports to be true which might break stuff. it would then need to "hack" a tsconfig.json to map web_modules/*.js to /web_modules/*.js, just trying to think of a way on top of my head
Would outputting a /web_modules/preact.d.ts next to /web_modules/preact.js work? I can't remember the conditions under which TypeScript automatically picks up adjacent d.ts files.
@Lange that's actually a great idea if it works... spinning up a test case now

Looks like it works!
Possible problems:
1 idea: instead of moving a type definition file, is there a way to write our own that says something like export * from 'package-name'; and then let TS resolve it correctly into the node_modules package?
Also I'll plug the babel plugin if you already have a build pipeline for your app. This would allow you to import packages by name (making TS happy) and then have babel rewrite them to "web_modules/" at build time
AFAIK, something like export * from 'preact' in /web_modules/preact.d.ts should work, but I'm not 100% certain. I'd help experiment but I'm currently crunching on an unrelated deadline 😩
Hmm, looks like it will work for some but not all:

Small update: using the babel plugin is giving me the best success right now. Still, would love to support TS without needing Babel. @bterlson @DanielRosenwasser any ideas?
Try using a flat install with yarn and add a path mapping from web_modules to node_modules in your tsconfig.json.
Ugh that got me so close:
// tsconfig.json
"baseUrl": ".",
"paths": {
"/web_modules/*": [
"web_modules/*",
"node_modules/*",
]
},
import * as preact from '/web_modules/preact.js';
// Loads JS correctly, but can't resolve to preact's package.json "typings" directory
import * as preact from '/web_modules/preact';
// Resolves to preact's package.json "typings" directory, but does not load JS correctly
got it!
"paths": {
"/web_modules/*.js": [
"node_modules/*",
"web_modules/*.js",
]
},

@DanielRosenwasser thanks for your help to get this far. Last remaining question is: ~@pika/web writes scoped packages to web_modules/@AUTHOR--PACKAGE-NAME.js with two dashes between the author and the package name. Would it be at all possible to resolve a pattern like that back to node_modules/@AUTHOR/PACKAGE-NAME/...? I can't think of how without regex, but this just seems to be using globs.~ Update: see next follow up comment.
Forgot to circle back to this! We no longer use that package name / -> -- rewriting, so I *THINK* the tsconfig.json paths snippet above works now. If anyone is able to confirm, I'll add a note to the README.
And I forgot to tag this issue.
You may look at #38 for a working example with typescript.
I was curious about this same thing, and I was wondering what it’d take to go the alternate route: use “old style” absolute imports, and have a Babel plugin transform them.
import { h } from 'preact'; // before
import { h } from './web_modules/preact'; // after
The idea is that TS has to spit out JS anyway, so it’s safe to transform after-the-fact. There’s a proof of concept here, along with a link to a very simple example: babel-plugin-import-pika-web.
Does this make sense? Was thinking it’d help the migration story, at least.
I think it does make sense. But, if you gonna use Babel to transpile the code, I'd skip the typescript compiler.
Keep the typescript syntax so you can get the tooling and use @babel/preset-typescript to transpile your project. Using Babel AFTER compiling from typescript I think is a little overkill. But should work anyway.
@FredKSchott I found the following tsconfig.json settings to most reliable...
"compilerOptions": {
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"/web_modules/*.js": [
"node_modules/@types/*",
"node_modules/*",
"web_modules/*.js"
]
},
... rest of tsconfig
}
This worked for react. I did find your above config settings to work well for preact.
Thanks @kloy! I've added a note to the README about TypeScript support, which includes your config snippet. LMK if I missed anything in the new section.
We've come a long way from the original issue. Now that we have a few different options for TS support, I'm going to close this issue and move any future discussion to a "TypeScript - General Discussion" thread on our new package community discussion board: https://www.pika.dev/packages/@pika/web/discuss/1087
Thanks again everyone!
Hi,
I've tried to leave a comment on the "TypeScript - General Discussion" but after signing in with my GitHub account it keeps asking me to sign in whenever I start writing (checked authorised apps, pika is present).
Back to thread, with the above config ("paths": ...) TS should be able to figure out the location of the declaration files, but it keeps complaining that none was found. Or do I still need to copy the declaration files to "web_modules"?
I have the following structure:
├── node_modules
├── dist
├── src/
│ ├── index.ts
├── web_modules
│ ├── lit-element.js
└── tsconfig.json
tsconfig
{
"compilerOptions": {
"target": "es2017",
"module": "esNext",
"moduleResolution": "node",
...
"outDir": "../backend/public/src",
"baseUrl": ".",
"paths": {
"/web_modules/*.js": [
"node_modules/@types/*",
"node_modules/*",
"web_modules/*.js"
]
},
"plugins": [
{ "name": "typescript-tslint-plugin" }
],
},
"include": [
"src/**/*.ts"
],
"exclude": []
}
index.ts
import { LitElement } from '../web_modules/lit-element.js';
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
error TS7016: Could not find a declaration file for module '../web_modules/lit-element.js'. '/Websites/.../frontend/web_modules/lit-element.js' implicitly has an 'any' type
Thanks!
@vedtam see how your import is for ../web_modules/? TypeScript isn’t applying the /web_modules/*.js resolution from tsconfig.json because the pattern doesn’t match. Delete the .. and TypeScript should be able to find it.
Also, inside the array, make sure node_modules/@types:*, node_modules/*, and web_modules/* are correct paths, relative to your baseUrl. But keep the top-level /web_modules/*.js as-is.
Think of this as an alias to TypeScript, basically. But it’s an alias that when it compiles, maps to an actual path for the browser.
@dangodev thanks mate, I've corrected the path and the app runs! But missing declaration files were still showing, than of curiosity I've reloaded TS config (switching to VS Code's version than back to workspace) and suddenly all errors went away...lol :D
Thanks!
Thanks @dangodev, and sorry about the sign-in issue @vedtam!
Most helpful comment
Looks like it works!