Skip to content

chore:Update for TS7 #16485

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

chore:Update for TS7 #16485

wants to merge 5 commits into from

Conversation

sandersn
Copy link

@sandersn sandersn commented Jul 23, 2025

I am testing Typescript 7's JS support, which I've largely rewritten during the switch to Go. That means Javascript code will need to change much more than Typescript code. Fortunately, most of the changes are for the better: Javascript semantics are now nearly identical to Typescript semantics. It's much stricter and no longer has some persistent bugs that arose from shady JS handling I wrote years ago.

This PR changes Svelte so that it compiles both with TS5.* and TS7, which means that occasionally there are duplicative or non-obvious changes. I'll annotate the interesting changes to explain why I made them.

Because TS7 is quite a way off, I don't know whether you'll want to take this PR. Most of the changes are for the better, because they're due to stricter TS-aligned checking. But some are neutral and there is the previously mentioned duplication in a few places.

Temporary note: if you try this with the nightly ts-native vscode extension, it will still be missing one or two bug fixes until today (2025-07-23) or tomorrow (2025-07-24).

sandersn added 2 commits July 23, 2025 08:02
I am testing Typescript 7's JS support, which I've largely rewritten
during the switch to Go. That means Javascript code will need to change
much more than Typescript code. Fortunately, most of the changes are for
the better: Javascript semantics are now nearly identical to Typescript
semantics. It's much stricter and no longer has some persistent bugs
that arose from shady JS handling I wrote years ago.

This PR changes Svelte so that it compiles both with TS5.* and TS7,
which means that occasionally there are duplicative or non-obvious
changes. I'll annotate the interesting changes to explain why I made them.

Because TS7 is quite a way off, I don't know whether you'll want to take
this PR. Most of the changes are for the better, because they're due to
stricter TS-aligned checking. But some are neutral and there is the
previously mentioned duplication in a few places.
Copy link

changeset-bot bot commented Jul 23, 2025

🦋 Changeset detected

Latest commit: c2f5e05

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
svelte Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

Playground

pnpm add https://pkg.pr.new/svelte@16485

@@ -26,7 +26,7 @@ await createBundle({
// so that types/properties with `@internal` (and its dependencies) are removed from the output
stripInternal: true,
paths: Object.fromEntries(
Object.entries(pkg.imports).map(([key, value]) => {
Object.entries(pkg.imports).map(/** @param {[string,any]} import */([key, value]) => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pkg.imports: any and Object.entries now infers [string, unknown] instead of [string, any]. This is stricter, and what happens in .ts files, but requires an explicit param type.
This shows up quite a bit in this PR.

@@ -9,8 +9,8 @@ import {
import { regex_ends_with_whitespace, regex_starts_with_whitespace } from '../../patterns.js';
import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js';

/** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS} NodeExistsValue */
/** @typedef {FORWARD | BACKWARD} Direction */
/** @typedef {typeof NODE_PROBABLY_EXISTS | typeof NODE_DEFINITELY_EXISTS} NodeExistsValue */
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JS type annotations no longer insert typeof in places where it finds a value but expects a type. You have to do it explicitly, like in TS.

@@ -14,6 +14,7 @@ export default function check_graph_for_cycles(edges) {
}, new Map());

const visited = new Set();
/** @type {Set<T>} */
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new Set(): Set<unknown> now, not Set<any>, so you have to explicitly provide a type. I think I gave real types throughout this PR rather than Set<any>, so the typing ends up better than before.

@@ -55,7 +55,7 @@ export function convert(source, ast) {

// Insert svelte:options back into the root nodes
if (/** @type {any} */ (options)?.__raw__) {
let idx = node.fragment.nodes.findIndex((node) => options.end <= node.start);
let idx = node.fragment.nodes.findIndex((node) => /** @type {any} */ (options).end <= node.start);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as before; options would be unknown in TS7 without this cast.

@@ -64,7 +64,7 @@ export function hmr(original, get_source) {
// @ts-expect-error
wrapper[FILENAME] = original[FILENAME];

// @ts-expect-error
// @ts-ignore
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error being ignored here is bogus, a result of wonky JS checking that mistakenly causes a circularity. In TS7, this bug is fixed. If this were a complete upgrade to TS7, I would remove this line, but since I want it to compile on TS5 and TS7, I switched it to ts-ignore.

@@ -160,10 +160,14 @@ export function createEventDispatcher() {
e.lifecycle_outside_component('createEventDispatcher');
}

/**
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same: signature-to-signature checking (like when you're checking that a returned function has the right type) is stricter in TS7, so you have to explicitly note that detail and options are optional.

I also improved the cast on type but I can't remember if that's actually necessary.

@@ -87,15 +88,15 @@ function normalize_html(window, html) {
/** @param {any} node */
function normalize_children(node) {
// sort attributes
const attributes = Array.from(node.attributes).sort((a, b) => {
const attributes = Array.from(node.attributes).sort((/** @type {any} */ a,/** @type {any} */ b) => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above; Array.from(node.attributes) infers unknown[] in TS7.

@@ -54,14 +56,24 @@ class Animation {

/**
* @param {HTMLElement} target
* @param {Keyframe[]} keyframes
* @param {{ duration: number, delay: number }} options
* @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of went overboard here fixing up this test to handle the DOM types. It's probably better to turn a bunch of these types to any instead. I needed to do something because of the improved signature arity checking in TS7.

@@ -189,13 +201,15 @@ function interpolate(a, b, p) {
* @param {{duration: number, delay: number}} options
* @returns {globalThis.Animation}
*/
// @ts-ignore
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the location of the error to be ignored moved in TS7 to match the location reported in a .ts file

@@ -43,6 +43,7 @@ export function create_deferred() {
/** @param {any} [reason] */
let reject = (reason) => {};

/** @type {Promise<any>} */
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TS7 inferred promise: Promise<unknown> without an explicit annotation.

if (arguments.length > 0) {
const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value;
return /** @type {() => V} */ (
function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after running prettier, Hide Whitespace is basically required to review this PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really excited to see TSGo hit the ground -- it's a gargantuan undertaking from you guys. We're glad we can be a bit of a guinea pig for typed JS! Feel free to run sveltejs/kit through its paces as well -- that one has some demons, especially in all of the crazy rootDirs and generated types stuff we do.

@@ -1,9 +1,9 @@
/** @import { Equals } from '#client' */

/** @type {Equals} */
export function equals(value) {
export const equals = function (value) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sigh, this is actually one of the few places I liked JSDoc more than TS -- the fact that there's no way to type function declarations in TS grinds my gears!

FWIW this actually is going to negatively impact us -- in our language tools, whenever you export function load from a +page file, we insert /** @type {Load} */ above it to provide you generated types without you having to explicitly import and use them. I'm sure we can find a workaround but it'll at least be somewhat more difficult now, as we can't just change function declarations to const {name} = () => {} as it's semantically different

@jakebailey
Copy link

especially in all of the crazy rootDirs and generated types stuff we do.

Just to note it, we don't yet do rootDirs because we thought we were going to remove that feature (it's wonky and usually has much better replacements, which IMO you all should consider, for the same reasons paths is usually the wrong tool), but since everyone hit it, we are going to have to implement it anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy