-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Reconsider the API for declaration files #2934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
That timing is so funny, I just posted #2935. Happy to help join that with this effort, if a new API could handle both "this import has this type def" + "the current file has this type def". |
@FredKSchott the problem with what you are suggesting in #2935 is that we need to know what to replace the JS file with in the compiler before we fetch the JS file. When retrieving a module, we could parse that module for the substitute and then go fetch the substitute before we hand it over to TypeScript, but that would be horribly inefficient. @ry the only other option is some sort of magic guessing, which we have tried to avoid... I am all for some alternative, but we have to have an alternative that works, which basically meets this functional requirement:
Things we probably shouldn't do:
What we currently have implements the behaviour resolve modules, which does a couple specific Deno things (like support fully qualified module specifiers) but also "caches" the map of |
It could be worth reaching out the TS core team, and see if there's any interest in working together on this to standardize something that suites Deno's needs and could be used by other projects as well. The only thing that I can think of is that I believe |
Yeah, good point. I will officially open a feature request and then champion via the back channels. I can see it would be generally useful, there are cases where the implicit resolution fails and explicit resolution is needed in other situations outside of Deno. |
Agree, this should be avoided.
I think this could be made to work so that the argument is a URL module specifier.
I don't particularly care about being compatible with existing code... : )
maybe also a solution... maybe not. |
That already works in Deno. It just that that directive means "pull in this other type file to be evaluated as part of the context", not "instead of loading and parsing this JS file, load this .d.ts file instead". We need the latter behaviour. The way it currently works in Also, not caring about existing code... then you end up in a situation where you can only run Deno TypeScript on Deno. We have #2644 specifically because we do care about existing code to an extend. 😁 |
@FredKSchott here is the issue I opened on microsoft/TypeScript#33437. My ramblings make sense to me, but we can see how the TypeScript team feels about it (and the wider TypeScript community). |
@ry @kitsonk @FredKSchott This already works in Deno. And works everywhere else. Examples: Untyped JS library, runtime error for bad argument: import { caught } from 'https://raw.github.com/rsp/t.ts/master/caught.js';
caught('wrong argument'); Importing a plain TS file that reexports the library as typed, correct compile time errors: import { caught } from 'https://raw.github.com/rsp/t.ts/master/caught.t.ts';
caught('wrong argument'); This is how import { caught as _caught } from './caught.js';
export const caught: <T>(promise: Promise<T>) => Promise<T> = _caught; Pros:
Cons:
Do you think that we could encourage using this style for writing new type definitions for Deno? I called it |
This is a big though. Could .t.ts be generated from .d.ts? |
Probably not right now out of the box (like .d.ts can be generated from .t.ts with a simple |
@hayd Now I'm thinking if maybe a .d.ts file can be included in .t.ts file without much modifications. Something like this: import { single as _single } from './single.js';
// @ts-ignore
export const single = _single;
// .d.ts file appended here:
// @ts-ignore
declare function single(a: string): boolean; The ts-ignore is because tsc complains about duplicate identifiers. Maybe someone else will be able to have some idea of how to overwrite types with something that is as similar as possible to a .d.ts syntax. |
This seems to work: single.t.ts: (Update: fixed order in example) // -----
// boilerplate:
import { single as _single } from './single.js';
// -----
// .d.ts file inserted here:
// @ts-ignore
declare function single(a: string): boolean;
// -----
// boilerplate:
// @ts-ignore
export const single: typeof single = _single;
// ----- and now my VS Code does what I want, i.e. shows me that the exported func is string => boolean and shows me an error, For someone who'd like to experiment: single.js: export function single(a) {
return a.length === 1;
} single-test.js: import { single } from './single.t.ts';
console.log(single(1)); (vs code underlines |
I'm not sure that is an improvement over the existing solution. This seems to require pre-processing of stuff, and not something that could be done on the fly, and would only work for Deno. The existing solution is effectively ignored by everyone else other than Deno. |
@kitsonk the experiments above are my attempts of being able to convert existing .d.ts to .t.ts as asked by @hayd but my original idea was to write .t.ts instead of .d.ts (in case we don't have any type definitions yet, or maybe it is not hard to do because the public api of a library is small). If there is a working .t.ts file then it should work not only for Deno (except the mandatory vs forbidden file extension in imports but that is true for all .ts files) because the result would be a plain ts file. Edit: I am not arguing that it is the way to go to convert existing .d.ts to .t.ts files on the fly, I am just brainstorming if that is even possible at all. What I am arguing is that if we didn't have exiting .d.ts files for something then I think it would be less hassle to write .t.ts files than to write .d.ts files, because there would be no problems with special comments/pragmas and I think the idea is simpler. |
I think we should be in a situation where someone doesn't have to produce something different. We should be able to consume For a real world example, a solution needs to make this use-case work, using the import * as _ from "https://cdn.pika.dev/lodash-es/^4.0.0"; And using the types from // @deno-types="https://unpkg.com/@types/lodash@^4.0.0/index.d.ts"
import * as _ from "https://cdn.pika.dev/lodash-es/^4.0.0"; Any replacement should be an improvement upon that, IMO. Don't forget, some types are authored as ambient/global and some are authored as UMD/modular. Both are currently supported and should be equally supported in the future. |
@kitsonk I agree with you. That's why my original question was "Is there any reason (other than already having d.ts files, but let's say that we are writing type definitions for an untyped JavaScript library from scratch) of using d.ts files instead of just having a TypeScript file that imports the JS symbols and re-export them as typed?" |
I've been thinking about this and I've had a thought of what we can do. I would still think that retaining the current functionality would be a good idea, but adding two other features to make supporting "coupling" JavaScript code with type definitions without introducing "resolution magic":
In both situations, Deno would fetch and return the "alternative" module to the TypeScript compiler, with the right media types (TypeScript instead of JavaScript) as the resolved module. |
+1 for both of the above. I could implement that header on the Pika CDN if that's the direction we want to go. |
How about add the path of .d.ts in the URL like: import {} from 'https://localhost/foo/bar.js#d.ts=/foo/bar.d.ts';
|
@kitsonk regarding your comment was there any progress? I want to give a hand towards the 1.0 release. |
It is next in my backlog. I am travelling at the moment and wouldn't get a chance to 30/31st of December now. It isn't the most straight forward of change to attempt as an early issue, as it requires both changes in Rust (fairly complex ones) and changes in the TS compiler. We really need to land the public compiler API before we do any more heavy lifting on the compiler also, but that is pending review by Ry. |
@kitsonk gotcha! |
Working on this now. |
Am I remembering correctly that import "./vendor/parser_typescript.d.ts"
import "./vendor/parser_typescript.js"; It's got an explicit file extension, after all. Was there any research into this? Or discussion with the TypeScript team? |
How do you know that the first import applies to the second? Also it would be breaking for other TypeScript compilers. True we have extensions, and that isn't supported, but there are plugins that can support that. The dual import would simply not. In the dual import, I haven't discussed with them, but it fundamentally modifies the AST, so that really isn't even viable. |
I would correlate them by the URL. If they have the same URL (except for extension, obviously), then they are linked, otherwise they are not. A simple rule that may not please every use case but it seems like a straightforward and reasonable rule to me. Plus it's better than custom syntax in a code comment, isn't it? |
We currently have an invented syntax for referencing a d.ts file. Here's an example:
I'm not so happy with this - but not sure what to replace it with. Adding this as an API to figure out before 1.0.
The text was updated successfully, but these errors were encountered: