Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions packages/core/rxjs-interop/test/rx_resource_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ describe('rxResource()', () => {
const appRef = TestBed.inject(ApplicationRef);
const request = signal(1);
let unsub = false;
let lastSeenRequest: number = 0;
let lastSeenRequest: number | null = 0;
rxResource({
params: request,
stream: ({params: request}) => {
stream: ({ params: request }) => {
lastSeenRequest = request;
return new Observable((sub) => {
if (request === 2) {
Expand Down Expand Up @@ -75,7 +75,7 @@ describe('rxResource()', () => {
expect(res.value()).toBe(3);

response.error('fail');
expect(res.error()).toEqual(jasmine.objectContaining({cause: 'fail'}));
expect(res.error()).toEqual(jasmine.objectContaining({ cause: 'fail' }));
expect(res.error()!.message).toContain('Resource');
});

Expand Down Expand Up @@ -114,6 +114,47 @@ describe('rxResource()', () => {

expect(() => rxRes.value()).toThrowError(/This is a FooError/);
});

it('should receive null params at runtime when params option is not provided', async () => {
const injector = TestBed.inject(Injector);
const appRef = TestBed.inject(ApplicationRef);
let receivedParams: unknown = 'NOT_SET';

const res = rxResource({
// No `params` option — defaults to () => null! internally
stream: ({ params }) => {
receivedParams = params;
return of('result');
},
injector,
});

await appRef.whenStable();
// When no params function is provided, the resource defaults to `() => null!` internally,
// so params inside the stream callback should be null at runtime.
expect(receivedParams).toBeNull();
expect(res.value()).toBe('result');
});

it('should type params as including null when params option can be undefined (issue #62724)', async () => {
const injector = TestBed.inject(Injector);

// This should compile without TypeScript errors.
// Before the fix, `params` was typed as `string` — missing `null`.
// After the fix, `params` is typed as `string | null`.
const getParamsFn = (): (() => string) | undefined => undefined;

// The key assertion: assigning `params` to `string | null` must compile.
rxResource({
params: getParamsFn(),
stream: ({ params }) => {
// TypeScript must allow: params is `string | null`, not just `string`
const _typeCheck: string | null = params;
return of(_typeCheck ?? 'fallback');
},
injector,
});
});
});

async function waitFor(fn: () => boolean): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/resource/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export interface ResourceRef<T> extends WritableResource<T> {
* @experimental
*/
export interface ResourceLoaderParams<R> {
params: NoInfer<Exclude<R, undefined>>;
params: NoInfer<Exclude<R, undefined | null> | null>;
abortSignal: AbortSignal;
previous: {
status: ResourceStatus;
Expand Down
Loading