From a2dfc997cc89883f8f28a77e438ec10a9c90bcd7 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Mon, 6 Oct 2025 21:49:50 +0200 Subject: [PATCH 1/3] fix: add TsrSerializable --- packages/router-core/src/index.ts | 2 + .../src/ssr/serializer/transformer.ts | 3 + packages/router-core/src/ssr/server.ts | 6 -- .../router-core/tests/serializer.test-d.ts | 74 +++++++++++++++++++ .../src/tests/createServerFn.test-d.ts | 19 ++++- 5 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 packages/router-core/tests/serializer.test-d.ts diff --git a/packages/router-core/src/index.ts b/packages/router-core/src/index.ts index 2bff87234d6..50273b30e0a 100644 --- a/packages/router-core/src/index.ts +++ b/packages/router-core/src/index.ts @@ -423,6 +423,8 @@ export type { SerializableExtensions, DefaultSerializable, Serializable, + TSR_SERIALIZABLE, + TsrSerializable, } from './ssr/serializer/transformer' export { diff --git a/packages/router-core/src/ssr/serializer/transformer.ts b/packages/router-core/src/ssr/serializer/transformer.ts index 4d0aaeffd44..512e52a9965 100644 --- a/packages/router-core/src/ssr/serializer/transformer.ts +++ b/packages/router-core/src/ssr/serializer/transformer.ts @@ -9,6 +9,8 @@ import type { import type { LooseReturnType } from '../../utils' import type { AnyRoute, ResolveAllSSR } from '../../route' +export const TSR_SERIALIZABLE = Symbol.for('TSR_SERIALIZABLE') +export type TsrSerializable = { [TSR_SERIALIZABLE]: true } export interface DefaultSerializable { number: number string: string @@ -17,6 +19,7 @@ export interface DefaultSerializable { undefined: undefined bigint: bigint Date: Date + TsrSerializable: TsrSerializable } export interface SerializableExtensions extends DefaultSerializable {} diff --git a/packages/router-core/src/ssr/server.ts b/packages/router-core/src/ssr/server.ts index 492762b092c..de39fb81367 100644 --- a/packages/router-core/src/ssr/server.ts +++ b/packages/router-core/src/ssr/server.ts @@ -8,9 +8,3 @@ export { transformReadableStreamWithRouter, } from './transformStreamWithRouter' export { attachRouterServerSsrUtils, getOrigin } from './ssr-server' - -// declare module '../router' { -// export interface RegisterSsr { -// ssr: true -// } -// } diff --git a/packages/router-core/tests/serializer.test-d.ts b/packages/router-core/tests/serializer.test-d.ts new file mode 100644 index 00000000000..726c582de80 --- /dev/null +++ b/packages/router-core/tests/serializer.test-d.ts @@ -0,0 +1,74 @@ +import { describe, expectTypeOf, it } from 'vitest' + +import { + Serializable, + TSR_SERIALIZABLE, + ValidateSerializable, + ValidateSerializableResult, +} from '../src/ssr/serializer/transformer' + +describe('Serializer', () => { + describe('Default types are serializable: $name', () => { + it('string', () => { + const value: string = 'hello' + expectTypeOf< + ValidateSerializableResult + >().toBeString() + }) + it('number', () => { + const value: number = 123 + + expectTypeOf< + ValidateSerializableResult + >().toBeNumber() + }) + it('boolean', () => { + const value: boolean = true + + expectTypeOf< + ValidateSerializableResult + >().toBeBoolean() + }) + it('null', () => { + const value = null + + expectTypeOf< + ValidateSerializableResult + >().toBeNull() + }) + it('undefined', () => { + const value = undefined + + expectTypeOf< + ValidateSerializableResult + >().toBeUndefined() + }) + it('bigint', () => { + const value = BigInt(123) + + expectTypeOf< + ValidateSerializableResult + >().toBeBigInt() + }) + it('Date', () => { + const value = new Date() + + expectTypeOf< + ValidateSerializableResult + >().toEqualTypeOf() + }) + }) + it('fails for non-serializable types', () => { + const value = () => {} + expectTypeOf< + ValidateSerializable + >().toEqualTypeOf<'Function is not serializable'>() + }) + + it('works for types extending TsrSerializable', () => { + type MyCustomType = { f: () => {} } & { [TSR_SERIALIZABLE]: true } + expectTypeOf< + ValidateSerializable + >().toEqualTypeOf() + }) +}) diff --git a/packages/start-client-core/src/tests/createServerFn.test-d.ts b/packages/start-client-core/src/tests/createServerFn.test-d.ts index 47718d74f2b..15367e4dbe0 100644 --- a/packages/start-client-core/src/tests/createServerFn.test-d.ts +++ b/packages/start-client-core/src/tests/createServerFn.test-d.ts @@ -2,7 +2,12 @@ import { describe, expectTypeOf, test } from 'vitest' import { createMiddleware } from '../createMiddleware' import { createServerFn } from '../createServerFn' import { TSS_SERVER_FUNCTION } from '../constants' -import type { Constrain, Register, Validator } from '@tanstack/router-core' +import type { + Constrain, + Register, + TsrSerializable, + Validator, +} from '@tanstack/router-core' import type { ConstrainValidator } from '../createServerFn' test('createServerFn method with autocomplete', () => { @@ -638,3 +643,15 @@ test('createServerFn returns sync array', () => { expectTypeOf(serverFn()).toEqualTypeOf>>() }) + +test('createServerFn respects TsrSerializable', () => { + type MyCustomType = { f: () => void; value: string } + type MyCustomTypeSerializable = MyCustomType & TsrSerializable + const fn1 = createServerFn().handler(() => { + const custom: MyCustomType = { f: () => {}, value: 'test' } + return { nested: { custom: custom as MyCustomTypeSerializable } } + }) + expectTypeOf(fn1()).toEqualTypeOf< + Promise<{ nested: { custom: MyCustomTypeSerializable } }> + >() +}) From 712b0a994fd1406a9741483ad05b2a17810b6174 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Mon, 6 Oct 2025 21:58:08 +0200 Subject: [PATCH 2/3] symbol is not used at runtime --- packages/router-core/src/ssr/serializer/transformer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/router-core/src/ssr/serializer/transformer.ts b/packages/router-core/src/ssr/serializer/transformer.ts index 512e52a9965..e20c2e544c3 100644 --- a/packages/router-core/src/ssr/serializer/transformer.ts +++ b/packages/router-core/src/ssr/serializer/transformer.ts @@ -9,7 +9,9 @@ import type { import type { LooseReturnType } from '../../utils' import type { AnyRoute, ResolveAllSSR } from '../../route' -export const TSR_SERIALIZABLE = Symbol.for('TSR_SERIALIZABLE') +declare const TSR_SERIALIZABLE: unique symbol +export type TSR_SERIALIZABLE = typeof TSR_SERIALIZABLE + export type TsrSerializable = { [TSR_SERIALIZABLE]: true } export interface DefaultSerializable { number: number From 7f454ed07f82cf731423efce1ac17283560ca1c2 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Mon, 6 Oct 2025 22:06:31 +0200 Subject: [PATCH 3/3] fix test --- packages/router-core/tests/serializer.test-d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/router-core/tests/serializer.test-d.ts b/packages/router-core/tests/serializer.test-d.ts index 726c582de80..4fd2c69faa1 100644 --- a/packages/router-core/tests/serializer.test-d.ts +++ b/packages/router-core/tests/serializer.test-d.ts @@ -2,7 +2,7 @@ import { describe, expectTypeOf, it } from 'vitest' import { Serializable, - TSR_SERIALIZABLE, + TsrSerializable, ValidateSerializable, ValidateSerializableResult, } from '../src/ssr/serializer/transformer' @@ -66,7 +66,7 @@ describe('Serializer', () => { }) it('works for types extending TsrSerializable', () => { - type MyCustomType = { f: () => {} } & { [TSR_SERIALIZABLE]: true } + type MyCustomType = { f: () => {} } & TsrSerializable expectTypeOf< ValidateSerializable >().toEqualTypeOf()