Skip to content

Commit f0a6b6d

Browse files
committed
feat: Restrict types for Spring and Tween
Closes Improve Spring data typing, or improving it so it doesn't bother about unsupported data types #14851
1 parent 32348a5 commit f0a6b6d

File tree

3 files changed

+33
-13
lines changed

3 files changed

+33
-13
lines changed

packages/svelte/src/motion/public.d.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ export interface Spring<T> extends Readable<T> {
2020
stiffness: number;
2121
}
2222

23+
/**
24+
* Defines the primitive data types that Spring and Tween objects can calculate on.
25+
*/
26+
export type MotionPrimitive = number | Date;
27+
28+
/**
29+
* Defines the type of objects Spring and Tween objects can work on.
30+
*/
31+
export interface MotionRecord {
32+
[x: string]: MotionPrimitive | MotionRecord | (MotionPrimitive | MotionRecord)[];
33+
}
34+
2335
/**
2436
* A wrapper for a value that behaves in a spring-like fashion. Changes to `spring.target` will cause `spring.current` to
2537
* move towards it over time, taking account of the `spring.stiffness` and `spring.damping` parameters.
@@ -36,7 +48,7 @@ export interface Spring<T> extends Readable<T> {
3648
* ```
3749
* @since 5.8.0
3850
*/
39-
export class Spring<T> {
51+
export class Spring<T extends MotionRecord[string]> {
4052
constructor(value: T, options?: SpringOpts);
4153

4254
/**
@@ -53,7 +65,7 @@ export class Spring<T> {
5365
* </script>
5466
* ```
5567
*/
56-
static of<U>(fn: () => U, options?: SpringOpts): Spring<U>;
68+
static of<U extends MotionRecord[string]>(fn: () => U, options?: SpringOpts): Spring<U>;
5769

5870
/**
5971
* Sets `spring.target` to `value` and returns a `Promise` that resolves if and when `spring.current` catches up to it.

packages/svelte/src/motion/spring.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @import { Task } from '#client' */
22
/** @import { SpringOpts, SpringUpdateOpts, TickContext } from './private.js' */
3-
/** @import { Spring as SpringStore } from './public.js' */
3+
/** @import { MotionRecord, Spring as SpringStore } from './public.js' */
44
import { writable } from '../store/shared/index.js';
55
import { loop } from '../internal/client/loop.js';
66
import { raf } from '../internal/client/timing.js';
@@ -58,7 +58,7 @@ function tick_spring(ctx, last_value, current_value, target_value) {
5858
* The spring function in Svelte creates a store whose value is animated, with a motion that simulates the behavior of a spring. This means when the value changes, instead of transitioning at a steady rate, it "bounces" like a spring would, depending on the physics parameters provided. This adds a level of realism to the transitions and can enhance the user experience.
5959
*
6060
* @deprecated Use [`Spring`](https://svelte.dev/docs/svelte/svelte-motion#Spring) instead
61-
* @template [T=any]
61+
* @template {MotionRecord[string]} [T=any]
6262
* @param {T} [value]
6363
* @param {SpringOpts} [opts]
6464
* @returns {SpringStore<T>}
@@ -159,17 +159,18 @@ export function spring(value, opts = {}) {
159159
* <input type="range" bind:value={spring.target} />
160160
* <input type="range" bind:value={spring.current} disabled />
161161
* ```
162-
* @template T
162+
* @template {MotionRecord[string]} T
163163
* @since 5.8.0
164164
*/
165165
export class Spring {
166166
#stiffness = source(0.15);
167167
#damping = source(0.8);
168168
#precision = source(0.01);
169169

170-
#current = source(/** @type {T} */ (undefined));
171-
#target = source(/** @type {T} */ (undefined));
172-
170+
#current;
171+
#target;
172+
173+
// @ts-expect-error Undefined doesn't satisfy the constraint of T.
173174
#last_value = /** @type {T} */ (undefined);
174175
#last_time = 0;
175176

@@ -187,7 +188,8 @@ export class Spring {
187188
* @param {SpringOpts} [options]
188189
*/
189190
constructor(value, options = {}) {
190-
this.#current.v = this.#target.v = value;
191+
this.#current = source(value);
192+
this.#target = source(value);
191193

192194
if (typeof options.stiffness === 'number') this.#stiffness.v = clamp(options.stiffness, 0, 1);
193195
if (typeof options.damping === 'number') this.#damping.v = clamp(options.damping, 0, 1);
@@ -207,7 +209,7 @@ export class Spring {
207209
* const spring = Spring.of(() => number);
208210
* </script>
209211
* ```
210-
* @template U
212+
* @template {MotionRecord[string]} U
211213
* @param {() => U} fn
212214
* @param {SpringOpts} [options]
213215
*/

packages/svelte/src/motion/tweened.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** @import { Task } from '../internal/client/types' */
2-
/** @import { Tweened } from './public' */
2+
/** @import { MotionRecord, Tweened } from './public' */
33
/** @import { TweenedOptions } from './private' */
44
import { writable } from '../store/shared/index.js';
55
import { raf } from '../internal/client/timing.js';
@@ -170,11 +170,15 @@ export function tweened(value, defaults = {}) {
170170
* <input type="range" bind:value={tween.target} />
171171
* <input type="range" bind:value={tween.current} disabled />
172172
* ```
173-
* @template T
173+
*
174+
* Refer to the `MotionRecord` type to understand all possible value types that Tween can handle.
175+
* @template {MotionRecord[string]} T
174176
* @since 5.8.0
175177
*/
176178
export class Tween {
179+
// @ts-expect-error Undefined doesn't satisfy the constraint of T.
177180
#current = source(/** @type {T} */ (undefined));
181+
// @ts-expect-error Undefined doesn't satisfy the constraint of T.
178182
#target = source(/** @type {T} */ (undefined));
179183

180184
/** @type {TweenedOptions<T>} */
@@ -205,7 +209,9 @@ export class Tween {
205209
* const tween = Tween.of(() => number);
206210
* </script>
207211
* ```
208-
* @template U
212+
*
213+
* Refer to the `MotionRecord` type to understand all possible value types that Tween can handle.
214+
* @template {MotionRecord[string]} U
209215
* @param {() => U} fn
210216
* @param {TweenedOptions<U>} [options]
211217
*/

0 commit comments

Comments
 (0)