Skip to content

Commit

Permalink
Fixed an issue with spawn within assign not returning a narrowed …
Browse files Browse the repository at this point in the history
…down `ActorRef` type on TypeScrip 5.0 (#4568)
  • Loading branch information
Andarist authored Dec 7, 2023
1 parent 1b09d69 commit a5c55fa
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/eighty-jars-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': patch
---

Fixed an issue with `spawn` within `assign` not returning a narrowed down `ActorRef` type on TypeScrip 5.0
10 changes: 9 additions & 1 deletion packages/core/src/spawn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,21 @@ type SpawnOptions<
>
: never;

// it's likely-ish that `(TActor & { src: TSrc })['logic']` would be faster
// but it's only possible to do it since https://github.com/microsoft/TypeScript/pull/53098 (TS 5.1)
// and we strive to support TS 5.0 whenever possible
type GetConcreteLogic<
TActor extends ProvidedActor,
TSrc extends TActor['src']
> = Extract<TActor, { src: TSrc }>['logic'];

export type Spawner<TActor extends ProvidedActor> = IsLiteralString<
TActor['src']
> extends true
? <TSrc extends TActor['src']>(
logic: TSrc,
...[options = {} as any]: SpawnOptions<TActor, TSrc>
) => ActorRefFrom<(TActor & { src: TSrc })['logic']>
) => ActorRefFrom<GetConcreteLogic<TActor, TSrc>>
: // TODO: do not accept machines without all implementations
<TLogic extends AnyActorLogic | string>(
src: TLogic,
Expand Down
47 changes: 47 additions & 0 deletions packages/core/test/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,53 @@ describe('spawner in assign', () => {
})
});
});

it(`should return a concrete actor ref type based on the used string reference`, () => {
const child = createMachine({
types: {} as {
context: {
counter: number;
};
},
context: {
counter: 100
}
});

const otherChild = createMachine({
types: {} as {
context: {
title: string;
};
},
context: {
title: 'The Answer'
}
});

createMachine({
types: {} as {
context: {
myChild?: ActorRefFrom<typeof child>;
};
actors:
| {
src: 'child';
logic: typeof child;
}
| {
src: 'other';
logic: typeof otherChild;
};
},
context: {},
entry: assign({
myChild: ({ spawn }) => {
return spawn('child');
}
})
});
});
});

describe('invoke', () => {
Expand Down

0 comments on commit a5c55fa

Please sign in to comment.